[net.sources] kernel and dump mods

chris@umcp-cs.UUCP (Chris Torek) (01/05/85)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting README'
sed 's/^X//' <<'//go.sysin dd *' >README
[README: 4.2 changes to kernel & dump for mass driver: 5 Jan 1984,
Chris Torek, University of Maryland.]

Before you do ANYTHING ELSE, read this file CAREFULLY!

This is the set of kernel changes needed to install the mass driver.
The mass driver itself consists of two files, sys/h/drivio.h and
sys/sys/dev_driv.c.  What the mass driver *does* is allow asynchronous
transfers.  (See the manual page for more information.)

Installing drivio.h and dev_driv.c is trivial; just plunk them down
and edit conf/files to include ``sys/dev_driv.c optional driv''.  (I
put it in at the end, but it doesn't really matter where it is.)

Modifying the existing source files to make the mass driver *work*
and *be useful* is more difficult.  First, SAVE COPIES OF ALL THE
FILES THAT WILL BE MODIFIED.  These are (if you don't bother deleting
extraneous ``#include "../h/conf.h"''s):

	h/conf.h		sys/vm_mem.c		vaxmba/ht.c
	h/fs.h			vax/conf.c		vaxmba/mba.c
	h/param.h		vax/machdep.c		vaxmba/mt.c
	h/vmparam.h		vax/ufs_machdep.c	vaxuba/ts.c

(Note that I have no changes for the tm driver.  If you have a tm,
you're on your own.  The changes should be similar to (if not exactly
the same as) those for the ts driver.)

Some of these changes are ``frills''; that is, they aren't essential to
the mass driver, but they sure don't hurt, and I think they are
useful.

Following the advice of a fellow netlander, I have provided context
diffs from 4.2 distribution sources.  HOWEVER, the diffs cannot be
applied by machine (e.g., by Larry Wall's patch program), because SOME
of the changes CANNOT be installed on a vanilla 4.2 system.  (In fact,
the mass driver source given here some two minor differences from mine:
my csleep()s have been edited back to sleep()s for this copy; the
bp->b_offset fields have been commented out.)

In particular, unless you have the BRL ``csleep'' routine in your
kernel, avoid changing any ``sleep'' calls to ``csleep''s.  (Csleep is
just ``commented sleep'', with an additional ``char *'' argument which
is stuck into the p_slmsg field of a struct proc.  You don't have this
field, of course . . . .)

I have tried to mark each set of changes as to which are necessary and
which are not.  However, a short description of the changes I made will
probably be helpful.

Basically, two things have been added to every character device:  an
optional ``d_mass'' routine and an optional ``d_bstrat'' routine.  If
the former is set, then the device is usable with the mass driver.  For
each transfer, the d_mass routine will be called on a buffer containing
the setup information which would normally be given to the block
device's strategy routine.  The d_mass routine should perform any
special actions required, and should trim the b_bcount field if
necessary.  (See the tsmass, htmass, or mtmass routines for examples of
what needs to be done on a tape drive.)  Right after the d_mass routine
returns, the d_bstrat routine (which is presumably the same as the
block device strategy routine) will be called to start the transfer.

Buffers in use for asynchronous transfers will be set up such that
the biodone() routine in ufs_bio.c will call the routine driviodone().
This examines the return information in the buffer and, if there is
more work to do, calls the d_mass and d_bstrat routines again.  This
creates a problem for the existing UNIBUS and MASSBUSS drivers.  Let's
look at the ts driver as a specific example.

When tsintr() is called, it sets um->um_tab.b_active to zero to
indicate that the device is no longer active.  After reading the device
registers and doing any necessary buffer cleanup, it will (if there is
another buffer in the queue) start another transfer right away by
calling tsstart(), then returns.  tsstart() sets um->um_tab.b_active,
calling the device active.  This works well enough in most cases.

However, now that we have the mass driver doing its thing, the
following sequence of events will occur.

	1. tsintr() sets um->um_tab.b_active = 0
	2. tsintr() calls biodone()
	3. biodone() calls driviodone()
	4. driviodone() calls tsstrategy()
	5. tsstrategy() puts the transfer at the end of the queue and
	   sees um->um_tab.b_active == 0, so calls tsstart()
	6. We return back to tsintr(), which sees that the queue is
	   not empty and calls tsstart(), clobbering the in-progress
	   transfer and having other disastrous effects.

Two solutions came immediately to mind: either have tsintr() check
um->um_tab.b_active before doing its last tsstart(), or have tsintr()
keep better track of the device activity.  In other words, if the
interupt service code isn't done yet, the device can be considered
active, since any requests placed on the device queue will be serviced
before tsintr() returns.  (Actually only the first will, but it has the
same effect.)  I prefer the latter solution, for various reasons, so
that is what I have done.

Interestingly, this code is centralized for massbuss devices (with the
interrupt code residing in vaxmba/mba.c) because there is only one
global massbuss interrupt vector, rather than lots of little vectors
for each device.  Nearly the same changes as were made to tsintr() had
to be made to mbaintr(), with some extra work required in mbstart() and
mbustart().

Well, now we have a working mass driver, but there is still one
problem:  dump can't use it, because dump wants to write 10240 byte
records on tapes, and the biggest buffers we can get from the buffer
cache are 8192 bytes.  This is easily fixed; just increase MAXBSIZE.
(I set it to 16384.)  But---that creates more problems of its own.
First of all, it cuts the number of buffers, making them too scarce.
After all, the ``average'' file system block is only about 4096 bytes
long anyway; we should have enough buffers for those, rather than for
16384 byte blocks.  Solution:  create an AVGBSIZE define in param.h,
and make the buffer allocation code use this.  Of course, that made
me look at the virtual memory setup code in vax/machdep.c, which I
just HAD to change all around :-), which led to making changes in
sys/vm_mem.c.

It turns out that as distributed, both 4.1 and 4.2 will not run on a
system with ``lots'' of memory, panic("sys pt too small")ing.  This is
silly.  If there is enough memory to map the kernel and SOME user area,
then that should be done, with the system in ``limp mode'' until the
kernel is recompiled with a bigger SYSPTSIZE.  I've changed the startup
code to notice slightly small or ridiculously large SYSPTSIZEs and
complain.  Of course, with all that extra virtual memory needed to
map 16384 byte buffers, I had to increase SYSPTSIZE (in vax/vmparam.h).

That covers every file but one: vax/ufs_machdep.c.  All I did there was
add a panic if the buffer size requested is > MAXBSIZE.  I made that
mistake the first time . . . .  (The change to h/fs.h is merely to fix
a comment; vax/conf.c needs to be changed because all the character
devices need two more entries.  Ok, so it covered every file but two.)

There is one other thing you can do while you're editing half your
kernel, though it's not important.  Far too many files needlessly
include ../h/conf.h.  A partial list of these files is given in the file
``needless_conf.h''.  If you're feeling particularly ambitious, now is
a good time to remove those includes, since everything is going to get
recompiled anyway.

Now that you've read all that,

	1) Install the drivio.h and dev_driv.c files and edit conf/files.
	2) Make kernel modifications as indicated in kernel_mods.
	   Don't forget to add the entry for the mass driver in
	   vax/conf.c, and do remember what it's major device number
	   is.
	3) Put a ``pseudo-device driv'' in your config file.
	4) Configure and compile a new kernel.
	5) Test the kernel to make sure it still works at all.

And for dump:

	1) Install the changes given in dump_mods.  (I'm sorry it's so
	   long; I fixed the formatting of lots of ``if(x) {''s or
	   ``while (y){''s which makes the diff listing a lot longer
	   than it really needs to be.)

	   All the non-mass-driver related changes in dump are from the
	   net (one from M. Kirk McKusick @ Berkeley, one from Donn
	   Seely @ Utah-CS, and one from ?(I forget) @ U of Waterloo,
	   as I recall).
	2) Compile, fix typos :-).  (Actually, I bet patch would work.)
	3) Make a /dev/driv0 with the right major device number (minor
	   == 0).  You may want to restrict access to it.
	4) Try ``dump <dump_options>m <regular dump stuff>'', e.g.,
	   dump 0m /, to make sure the mass driver works.
	5) Edit the manual entry to note the new ``m'' key for invoking
	   the mass driver.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 README
	/bin/echo -n '	'; /bin/ls -ld README
fi
/bin/echo 'Extracting dev_driv.c'
sed 's/^X//' <<'//go.sysin dd *' >dev_driv.c
#ifndef lint
static char rcsid[] = "$Header: /usr/sys/sys/RCS/dev_driv.c,v 1.3 85/01/03 12:27:49 chris Exp $";
#endif lint

X/*
 * dev_driv.c ("mass driver")
 * Chris Torek, U of Maryland
 * Pseudo-driver for running other devices asynchronously
 *
 * Copyright (c) 1985, all rights reserved.  The right to distribute this
 * code is explicitly granted if and only if 1) no changes are made, or
 * 2) all changes are clearly marked (e.g., via #ifdef/#endif).  (All I'm
 * trying to do here is make sure my name doesn't get sent around on some
 * piece of code I never wrote.  OK?  PLEEEEZE???  :-) )
 *
 * This pseudo driver allows one to read and write a device without
 * waiting for the transfer(s) to complete.  It calls the device's (block)
 * strategy routine directly.  We only run with specially-prepared
 * drivers, however: there is a new routine (d_mass) we use as a
 * combination of "minphys" and a catch-all for anything else that must be
 * done before starting a transfer, and there must be a strategy routine
 * in the character switch entry.  Note that we also deal in transfers of
 * other than BLKDEV_IOSIZE bytes.
 *
 * An initial ioctl() sets up the maximum number and size of transfers.
 * Space is stolen from the buffer pool (but never more than half
 * of the real buffer pool may be in use).  When the pseudo-driver
 * is read or written, the data is copied in to kernel space and
 * we start (but do not wait for) the real transfer.
 *
 * (READ IS NOT DONE YET)
 * For read()s, an ioctl() can be used to start a transfer.  Later
 * (when select() says it's OK) a read() will grab the data.
 *
 * For write()s, the transfer is started after copying from user
 * space.  Select() will succeed when the write is done.
 *
 * In any case, an ioctl() is used to get the final return value
 * (consisting of a real return value and an error code).  Another
 * ioctl() can be used to wait for all pending transfers to complete.
 *
 * We shamelessly use the (otherwise useless to us) b_proc field of the
 * buffers to hold the info we need when our iodone routine is called.
 *
 * Problem: since kernel buffer space is allocated based on MAXBSIZE
 * as the maximum amount of kernel space in any one ``struct buf'',
 * that is the largest transfer size we can handle as well.
 *
 * TODO:
 *	- Write read() code
 *	- Stop transfers at error (provide ioctl to clear error condition?)
 *	- Learn why we sometimes hang before closing when using MAX_XFERS
 */

#include "driv.h"
#if NDRIV > 0

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/buf.h"
#include "../h/inode.h"
#include "../h/file.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/uio.h"
#include "../h/ioctl.h"
#include "../h/conf.h"
#include "../h/errno.h"

#include "../h/drivio.h"

X/* one of the hard limits (the other is MAXBSIZE) */
#define MAX_XFERS	25	/* max # of pending transfers */

X/*
 * The xfer structure is used to hold all the info needed about ongoing
 * and completed transfers.
 * N.B.: Assumes MAXBSIZE fits in an "int".
 */
struct xfer {
	struct	buf *xf_buf;	/* buffer */
	struct	driv_softc *xf_sc;/* backpointer to driver info */
	caddr_t	xf_addr;	/* base of original transfer */
	short	xf_rw;		/* B_READ or B_WRITE */
	short	xf_error;	/* error number (if any) */
	off_t	xf_offset;	/* position at start of transfer */
	int	xf_bcount;	/* original byte count */
	int	xf_resid;	/* remaining byte count */
	int	xf_xcount;	/* bp->b_bcount, in case it gets munched */
	int	xf_rval;	/* return value */
};

struct driv_softc {
	short	sc_flags;	/* state info */
	dev_t	sc_dev;		/* device to which to refer */
	struct	file *sc_fp;	/* user file descriptor to manipulate */
	int	sc_xsize;	/* amount of memory allocated per xfer */
	short	sc_max_xfers;	/* max # of pending transfers */
	short	sc_pend;	/* number of pending transfers */
	short	sc_nxx;		/* index of next transfer */
	short	sc_rxx;		/* index of next return val */
	short	sc_valid;	/* number of valid return values */
	struct	xfer sc_xf[MAX_XFERS];/* transfer info */
	struct	proc *sc_sel;	/* process selecting */
} driv_softc[NDRIV];

#define DRIV_OPEN	0x01	/* open */
#define DRIV_DEV	0x02	/* have sc_dev info etc. */
#define DRIV_WANT	0x04	/* wakeup() when I/O is done */
#define DRIV_SELCOLL	0x08	/* select() collision */

#define PDRIV (PZERO-1)		/* priority for sleep() on buffers */

extern int bufpages;		/* tells us how big the buffer pool is */
int drivpages;			/* total number of pages stolen from the
				   buffer pool at the moment */
extern int nbuf;		/* how many buffers are there? */
int drivbufs;			/* how many do we have? */

X/*
 * Open the pseudo-driver
 */
X/*ARGSUSED*/
drivopen(dev, flag)
dev_t dev;
int flag;
{
	register struct driv_softc *sc = &driv_softc[minor(dev)];

	if (sc->sc_flags & DRIV_OPEN)
		return (EBUSY);
	sc->sc_flags = DRIV_OPEN;
	sc->sc_max_xfers = 0;
	sc->sc_xsize = 0;
	sc->sc_pend = 0;
	sc->sc_nxx = 0;
	sc->sc_rxx = 0;
	sc->sc_valid = 0;
	return (0);
}

X/*
 * Close the pseudo-driver
 */
X/*ARGSUSED*/
drivclose(dev, flag)
dev_t dev;
int flag;
{
	register struct driv_softc *sc = &driv_softc[minor(dev)];

	if (sc->sc_flags & DRIV_DEV) {
		drivfree(sc);
		closef(sc->sc_fp);
	}
	sc->sc_flags = 0;
}

X/*
 * Read.  We don't do this yet...
 */
X/*ARGSUSED*/
drivread(dev, uio)
dev_t dev;
struct uio *uio;
{

	return (EOPNOTSUPP);	/* XXX */
}

X/*
 * Write.  Dump the user's write stuff into our buffer, then start
 * things rolling.
 */
drivwrite(dev, uio)
dev_t dev;
register struct uio *uio;
{
	register struct driv_softc *sc = &driv_softc[minor(dev)];
	register struct xfer *xf;
	int s, error;

	if ((sc->sc_flags & DRIV_DEV) == 0)
		return (EINVAL);
	if ((sc->sc_fp->f_flag & FWRITE) == 0)
		return (EBADF);
	if (sc->sc_xsize < uio->uio_resid)
		return (ENOMEM);

	s = spl6();
	while (sc->sc_pend >= sc->sc_max_xfers) {
		sc->sc_flags |= DRIV_WANT;
		sleep((caddr_t)sc, PDRIV);
	}
	splx(s);
	xf = &sc->sc_xf[sc->sc_nxx++];
	if (sc->sc_nxx >= sc->sc_max_xfers)
		sc->sc_nxx = 0;
	if (xf->xf_addr == NULL)
		panic("drivwrite");
	xf->xf_offset = uio->uio_offset;
	error = uiomove(xf->xf_addr, xf->xf_bcount = uio->uio_resid,
	    UIO_WRITE, uio);
	if (error)
		return (error);
	drivstart(xf, B_WRITE);
	return (0);
}

X/*
 * Handle an ioctl.
 */
drivioctl(dev, cmd, data, flag)
dev_t dev;
int cmd;
register caddr_t data;
int flag;
{
	register struct driv_softc *sc = &driv_softc[minor(dev)];

	switch (cmd) {

	case DIOCSETINFO: {	/* set device info */
		register struct file *fp;
		register struct inode *ip;
		int nxfer;
#define d ((struct diocinfo *)data)

		if (sc->sc_flags & DRIV_DEV)
			return (EINVAL);/* EEXIST? */
		nxfer = d->dioc_nxfer > MAX_XFERS ? MAX_XFERS : d->dioc_nxfer;
		if (d->dioc_xsize < 1 || nxfer < 1)
			return (EINVAL);
		if (d->dioc_xsize > MAXBSIZE)
			return (ENOSPC);/* sort of */
		fp = getf(d->dioc_fd);
		if (fp == NULL)
			return (EBADF);
		if (fp->f_type != DTYPE_INODE)
			return (EINVAL);
		ip = (struct inode *)fp->f_data;
		if ((ip->i_mode & IFMT) != IFCHR ||
		    cdevsw[major((dev_t)ip->i_rdev)].d_mass == NULL)
			return (EINVAL);
		fp->f_count++;	/* prevent user from close()ing */
		if (drivalloc(sc, d->dioc_xsize, nxfer)) {
			closef(fp);
			return (ENOMEM);
		}
		sc->sc_flags |= DRIV_DEV;
		sc->sc_fp = fp;
		sc->sc_dev = (dev_t)ip->i_rdev;
		sc->sc_xsize = d->dioc_xsize;
		sc->sc_max_xfers = nxfer;
		break;
#undef d
	}

	case DIOCREAD:		/* start a read */
		return (EOPNOTSUPP);/* XXX */

	case DIOCGETRET: {	/* get return value */
		register struct xfer *xf;
#define d ((struct diocret *)data)

		if (sc->sc_valid == 0)
			return (ENOENT);/* well, sorta */
		xf = &sc->sc_xf[sc->sc_rxx++];
		if (sc->sc_rxx >= sc->sc_max_xfers)
			sc->sc_rxx = 0;
		sc->sc_valid--;
		d->dioc_rval = xf->xf_rval;
		d->dioc_error = xf->xf_error;
		break;
#undef d
	}

	case DIOCWAIT: {	/* wait for pending transfers */
		register int s = spl6();

		while (sc->sc_pend) {
			sc->sc_flags |= DRIV_WANT;
			sleep((caddr_t)sc, PZERO+1);
		}
		splx(s);	/* == spl0()? */
		break;
	}

	case DIOCGMAXX:		/* get max # transfers */
		*(int *)data =
		    (sc->sc_flags & DRIV_DEV) ? sc->sc_max_xfers : MAX_XFERS;
		break;

	default:
		return (ENOTTY);
	}
	return (0);
}

X/*
 * Select.
 */
drivselect(dev, rw)
dev_t dev;
int rw;
{
	register struct driv_softc *sc = &driv_softc[minor(dev)];
	int s = spl6();

	switch (rw) {

	case FREAD:
		return (1);	/* XXX */

	case FWRITE:
		if (sc->sc_valid) {
			splx(s);
			return (1);
		}
		if (sc->sc_sel && sc->sc_sel->p_wchan == (caddr_t)&selwait)
			sc->sc_flags |= DRIV_SELCOLL;
		else
			sc->sc_sel = u.u_procp;
		splx(s);
		return (0);

	default:
		splx(s);
		return (0);
	}
	/*NOTREACHED*/
}

X/*
 * Start a transfer.  Fill in all the buffer fields and some of the xf
 * fields, and make sure the device is active.
 */
drivstart(xf, rw)
register struct xfer *xf;
int rw;
{
	register struct driv_softc *sc = xf->xf_sc;
	register struct buf *bp = xf->xf_buf;
	int s, driviodone();

	if (bp == NULL)		/* just in case... */
		panic("drivstart bp");
	if (sc == NULL)
		panic("drivstart sc");
	if (bp->b_flags & B_BUSY)
		panic("drivstart B_BUSY");
	bp->b_un.b_addr = xf->xf_addr;/* set buffer address */
	xf->xf_resid = xf->xf_bcount;
	xf->xf_rw = rw;
	s = spl6();
	if (sc->sc_pend++) {	/* already active */
		splx(s);
		return;
	}
	splx(s);
	drivgo(sc->sc_dev, bp, xf);
}

X/*
 * Fill in the buffer fields and call the strategy routine.
 */
drivgo(dev, bp, xf)
dev_t dev;
register struct buf *bp;
register struct xfer *xf;
{
	register struct cdevsw *d = &cdevsw[major(dev)];
	int driviodone();

	bp->b_proc = (struct proc *)xf;/* ick */
	bp->b_error = 0;
	bp->b_iodone = driviodone;
	bp->b_flags = B_BUSY | B_CALL | xf->xf_rw;
	bp->b_dev = dev;
X/*	bp->b_offset = xf->xf_offset;			/* BRL */
	bp->b_blkno = btodb(xf->xf_offset);
	bp->b_bcount = xf->xf_resid;
	(*d->d_mass)(bp);
	xf->xf_xcount = bp->b_bcount;
	(*d->d_bstrat)(bp);
}

X/*
 * The transfer involving this buffer is done.  If there is more work
 * to do, fill in the buffer info and restart the transfer.  Otherwise,
 * wake up anyone sleeping on the buffer or the transfer, and wake up
 * anyone doing select()s, then if there are more transfers pending,
 * fire up the next one.
 */
driviodone(bp)
register struct buf *bp;
{
	register struct xfer *xf = (struct xfer *)bp->b_proc;/* ick */
	register struct driv_softc *sc;
	register int c;

	sc = xf->xf_sc;
	c = xf->xf_xcount - bp->b_resid;
	xf->xf_resid -= c;
	xf->xf_offset += c;
	if (xf->xf_resid > 0) {	/* more to do */
		/* temp kludge for tape drives */
		if (bp->b_resid || (bp->b_flags&B_ERROR))
			goto done;
		bp->b_un.b_addr += c;	/* advance */
		drivgo(sc->sc_dev, bp, xf);
		return;
	}
done:
	if (bp->b_flags & B_WANTED)
		wakeup((caddr_t)bp);
	bp->b_flags &= ~(B_BUSY|B_WANTED);
	if (sc->sc_flags & DRIV_WANT) {
		wakeup((caddr_t)sc);
		sc->sc_flags &= ~DRIV_WANT;
	}
	xf->xf_rval = xf->xf_bcount - xf->xf_resid;
	xf->xf_error = geterror(bp);
	sc->sc_valid++;
	if (sc->sc_valid > sc->sc_max_xfers) {/* user just lost a return val */
		sc->sc_valid--;
		if (++sc->sc_rxx >= sc->sc_max_xfers)
			sc->sc_rxx = 0;
	}
	if (sc->sc_sel) {
		selwakeup(sc->sc_sel, sc->sc_flags & DRIV_SELCOLL);
		sc->sc_sel = NULL;
	}
	if (--sc->sc_pend > 0) {	/* start the next one */
		c = (xf - sc->sc_xf) + 1;
		if (c >= sc->sc_max_xfers)
			c = 0;
		xf = &sc->sc_xf[c];
		bp = xf->xf_buf;
		if (bp == NULL)
			panic("driviodone bp");
		drivgo(sc->sc_dev, bp, xf);
	}
}

X/*
 * Allocate enough buffer space for the given totals, but not if
 * it would take too many buffers or too much memory.  Fill in the
 * xf_addr fields.  To be nice to the paging system, we round up
 * to CLBYTES, though it's not supposed to be necessary.
 */
drivalloc(sc, xsize, nxfer)
register struct driv_softc *sc;
register int xsize, nxfer;
{
	register int i;
	register struct xfer *xf;

	if ((drivbufs + nxfer) >= (nbuf >> 1))
		return (-1);
	xsize = roundup(xsize, CLBYTES);
	i = xsize / CLBYTES * nxfer + drivpages;
	if (i >= (bufpages >> 1))
		return (-1);
	drivbufs += nxfer;
	drivpages = i;
	for (i = 0, xf = sc->sc_xf; i < nxfer; i++, xf++) {
		xf->xf_buf = geteblk(xsize);
		xf->xf_buf->b_flags &= ~B_BUSY;	/* tyke it awye! */
		xf->xf_addr = xf->xf_buf->b_un.b_addr;/* save buffer addr */
		xf->xf_sc = sc;
	}
	return (0);
}

X/*
 * Free the attached buffers, zapping the xf pointers so as to detect
 * bugs early.
 */
drivfree(sc)
register struct driv_softc *sc;
{
	register int i;
	register struct xfer *xf;
	register struct buf *bp;

	i = spl6();
	while (sc->sc_pend) {
		sc->sc_flags |= DRIV_WANT;
		sleep((caddr_t)sc, PDRIV);
	}
	splx(i);
	for (i = 0, xf = sc->sc_xf; i < sc->sc_max_xfers; i++, xf++) {
		if ((bp = xf->xf_buf) == NULL)
			panic("drivfree");
		drivpages -= bp->b_bufsize / CLBYTES;
		drivbufs--;

		/* return it in its original condition */
		bp->b_dev = NODEV;
		bp->b_flags = B_BUSY|B_INVAL;
		bp->b_un.b_addr = xf->xf_addr;
		bp->b_bcount = bp->b_bufsize;
		brelse(bp);

		xf->xf_buf = NULL;
		xf->xf_addr = NULL;
		xf->xf_sc = NULL;
	}
}
#endif NDRIV > 0
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 dev_driv.c
	/bin/echo -n '	'; /bin/ls -ld dev_driv.c
fi
/bin/echo 'Extracting drivio.h'
sed 's/^X//' <<'//go.sysin dd *' >drivio.h
X/* $Header: /usr/sys/h/RCS/drivio.h,v 1.1 85/01/02 09:44:05 chris Exp $ */

X/*
 * diocinfo contains the information needed to initialize the hyperdriver
 */
struct diocinfo {
	int	dioc_fd;	/* file descriptor */
	int	dioc_xsize;	/* max size of any one transfer */
	int	dioc_nxfer;	/* max number of uncompleted transfers */
};

X/*
 * diocret contains the return status information for completed transfers
 */
struct diocret {
	int	dioc_rval;	/* return value */
	int	dioc_error;	/* error number */
};

#define DIOCSETINFO _IOW(D, 0, struct diocinfo)	/* set info */
#define DIOCREAD    _IO(D, 1)			/* start a read */
#define DIOCGETRET  _IOR(D, 2, struct diocret)	/* get return info */
#define DIOCWAIT    _IO(D, 3)			/* wait for I/O to finish */
#define DIOCGMAXX   _IOR(D, 4, int)		/* get max # xfers */
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 drivio.h
	/bin/echo -n '	'; /bin/ls -ld drivio.h
fi
/bin/echo 'Extracting dump_mods'
sed 's/^X//' <<'//go.sysin dd *' >dump_mods
RCS file: RCS/dump.h,v
retrieving revision 1.1
diff -c1 -r1.1 dump.h
*** /tmp/,RCSt1005006	Sat Jan  5 03:22:00 1985
--- dump.h	Sun Dec 30 07:34:11 1984
***************
*** 69,70
  int	dump();
  int	tapsrec();

--- 69,71 -----
  int	dump();
+ int	dirdump();
  int	tapsrec();
RCS file: RCS/dumpitime.c,v
retrieving revision 1.1
diff -c1 -r1.1 dumpitime.c
*** /tmp/,RCSt1005011	Sat Jan  5 03:22:06 1985
--- dumpitime.c	Sun Dec 30 07:34:12 1984
***************
*** 206,209
   * This is an estimation of the number of TP_BSIZE blocks in the file.
!  * It assumes that there are no unallocated blocks; hence
!  * the estimate may be high
   */

--- 206,209 -----
   * This is an estimation of the number of TP_BSIZE blocks in the file.
!  * WARNING: it uses the di_blocks field of the inode, which is not
!  * maintained in 4.1c BSD.
   */
***************
*** 212,214
  {
! 	long s;
  

--- 212,214 -----
  {
! 	long s, t;
  
***************
*** 215,218
  	esize++;
! 	/* calc number of TP_BSIZE blocks */
! 	s = howmany(ip->di_size, TP_BSIZE);
  	if (ip->di_size > sblock->fs_bsize * NDADDR) {

--- 215,236 -----
  	esize++;
! 
! 	/*
! 	 * ip->di_size is the size of the file in bytes.
! 	 * ip->di_blocks stores the number of sectors actually in the file.
! 	 * If there are more sectors than the size would indicate, this just
! 	 *	means that there are unused sectors in the last file block;
! 	 *	we can safely ignore these (s = t below).
! 	 * If the file is bigger than the number of sectors would indicate,
! 	 *	then the file has holes in it.  In this case we must use the
! 	 *	block count for keeping track of actual blocks used, but we
! 	 *	use the actual size for estimating the number of indirect
! 	 *	blocks (t vs. s in the indirect block calculations) -- this
! 	 *	is simply because dump fills in holes in indirect blocks,
! 	 *	so the di_blocks field (which already counts the indirect
! 	 *	blocks) can't be used directly.
! 	 */
! 	 s = howmany(dbtob(ip->di_blocks), TP_BSIZE);
! 	 t = howmany(ip->di_size, TP_BSIZE);
! 	 if (s > t)
! 		s = t;
  	if (ip->di_size > sblock->fs_bsize * NDADDR) {
***************
*** 218,221
  	if (ip->di_size > sblock->fs_bsize * NDADDR) {
! 		/* calc number of indirect blocks on the dump tape */
! 		s += howmany(s - NDADDR * sblock->fs_bsize / TP_BSIZE,
  			TP_NINDIR);

--- 236,239 -----
  	if (ip->di_size > sblock->fs_bsize * NDADDR) {
! 		/* calculate the number of indirect blocks on the dump tape */
! 		s += howmany(t - NDADDR * sblock->fs_bsize / TP_BSIZE,
  			TP_NINDIR);
RCS file: RCS/dumpmain.c,v
retrieving revision 1.1
diff -c1 -r1.1 dumpmain.c
*** /tmp/,RCSt1005016	Sat Jan  5 03:22:14 1985
--- dumpmain.c	Mon Dec 31 23:06:55 1984
***************
*** 9,10
  int	cartridge = 0;	/* Assume non-cartridge tape */
  #ifdef RDUMP

--- 9,11 -----
  int	cartridge = 0;	/* Assume non-cartridge tape */
+ int	mass_driver = 0;/* use mass driver */
  #ifdef RDUMP
***************
*** 36,38
  	arg = "u";
! 	if(argc > 1) {
  		argv++;

--- 37,39 -----
  	arg = "u";
! 	if (argc > 1) {
  		argv++;
***************
*** 43,45
  	}
! 	while(*arg)
  	switch (*arg++) {

--- 44,46 -----
  	}
! 	while (*arg)
  	switch (*arg++) {
***************
*** 55,57
  	case 'f':			/* output file */
! 		if(argc > 1) {
  			argv++;

--- 56,58 -----
  	case 'f':			/* output file */
! 		if (argc > 1) {
  			argv++;
***************
*** 71,73
  	case 's':			/* tape size, feet */
! 		if(argc > 1) {
  			argv++;

--- 72,74 -----
  	case 's':			/* tape size, feet */
! 		if (argc > 1) {
  			argv++;
***************
*** 80,82
  	case 'b':			/* blocks per tape write */
! 		if(argc > 1) {
  			argv++;

--- 81,83 -----
  	case 'b':			/* blocks per tape write */
! 		if (argc > 1) {
  			argv++;
***************
*** 112,113
  
  	default:

--- 113,118 -----
  
+ 	case 'm':
+ 		mass_driver++;		/* the accelerator... */
+ 		break;
+ 
  	default:
***************
*** 116,118
  	}
! 	if(argc > 1) {
  		argv++;

--- 121,123 -----
  	}
! 	if (argc > 1) {
  		argv++;
***************
*** 267,269
  	msg("dumping (Pass III) [directories]\n");
! 	pass(dump, dirmap);
  

--- 272,274 -----
  	msg("dumping (Pass III) [directories]\n");
! 	pass(dirdump, dirmap);
  
RCS file: RCS/dumptape.c,v
retrieving revision 1.1
diff -c1 -r1.1 dumptape.c
*** /tmp/,RCSt1005040	Sat Jan  5 03:23:00 1985
--- dumptape.c	Mon Dec 31 23:40:23 1984
***************
*** 2,3
  #include "dump.h"
  

--- 2,6 -----
  #include "dump.h"
+ #include <sys/ioctl.h>
+ #include <sys/drivio.h>
+ #include <errno.h>
  
***************
*** 7,8
  extern int ntrec;		/* blocking factor on tape */
  

--- 10,12 -----
  extern int ntrec;		/* blocking factor on tape */
+ extern int mass_driver;		/* true => should use mass driver */
  
***************
*** 8,9
  
  /*

--- 12,19 -----
  
+ int	wr_pend;		/* number of pending writes */
+ int	wr_maxpend;		/* max allowable # of pending writes */
+ int	WR_MAX = 0;		/* patchable, can set max we will use */
+ int	drivfd = -1;		/* driver fd */
+ extern int errno;
+ 
  /*
***************
*** 29,31
  {
- 	register i;
  

--- 39,40 -----
  {
  
***************
*** 31,34
  
! 	for (i=0; i < TP_BSIZE; i++)
! 		tblock[trecno][i] = *dp++;
  	trecno++;

--- 40,42 -----
  
! 	bcopy(dp, tblock[trecno], TP_BSIZE);
  	trecno++;
***************
*** 35,38
  	spcl.c_tapea++;
! 	if(trecno >= ntrec)
! 		flusht();
  }

--- 43,46 -----
  	spcl.c_tapea++;
! 	if (trecno >= ntrec)
! 		t_flusht(1, 0);
  }
***************
*** 40,42
  dmpblk(blkno, size)
! 	daddr_t blkno;
  	int size;

--- 48,50 -----
  dmpblk(blkno, size)
! 	register daddr_t blkno;
  	int size;
***************
*** 43,45
  {
! 	int avail, tpblks, dblkno;
  

--- 51,53 -----
  {
! 	register int avail, tpblks, dblkno;
  
***************
*** 53,55
  		spcl.c_tapea += avail;
! 		flusht();
  		dblkno += avail * (TP_BSIZE / DEV_BSIZE);

--- 61,63 -----
  		spcl.c_tapea += avail;
! 		t_flusht(1, 0);
  		dblkno += avail * (TP_BSIZE / DEV_BSIZE);
***************
*** 61,64
  	spcl.c_tapea += tpblks;
! 	if(trecno >= ntrec)
! 		flusht();
  }

--- 69,72 -----
  	spcl.c_tapea += tpblks;
! 	if (trecno >= ntrec)
! 		t_flusht(1, 0);
  }
***************
*** 67,69
  
! flusht()
  {

--- 75,81 -----
  
! /*
!  * Write and/or wait for previous writes.
!  */
! t_flusht(dowrite, dowait)
! 	int dowrite, dowait;
  {
***************
*** 69,71
  {
! 	register i, si;
  	daddr_t d;

--- 81,83 -----
  {
! 	register int i, si;
  	daddr_t d;
***************
*** 72,73
  
  	trecno = 0;

--- 84,91 -----
  
+ 	if (dowait && mass_driver && !pipeout) {
+ 		if (wrwait())
+ 			goto tape_err;
+ 	}
+ 	if (!dowrite)
+ 		return;
  	trecno = 0;
***************
*** 73,100
  	trecno = 0;
! 	if (write(to, tblock[0], writesize) != writesize){
! 		if (pipeout) {
! 			msg("Tape write error on %s\n", tape);
! 			msg("Cannot recover\n");
! 			dumpabort();
! 			/* NOTREACHED */
! 		}
! 		msg("Tape write error on tape %d\n", tapeno);
! 		broadcast("TAPE ERROR!\n");
! 		if (query("Do you want to restart?")){
! 			msg("This tape will rewind.  After it is rewound,\n");
! 			msg("replace the faulty tape with a new one;\n");
! 			msg("this dump volume will be rewritten.\n");
! 			/*
! 			 *	Temporarily change the tapeno identification
! 			 */
! 			tapeno--;
! 			nogripe = 1;
! 			close_rewind();
! 			nogripe = 0;
! 			tapeno++;
! 			Exit(X_REWRITE);
! 		} else {
! 			dumpabort();
! 			/*NOTREACHED*/
! 		}
  	}

--- 91,95 -----
  	trecno = 0;
! 	if (mass_driver && !pipeout) {
! 		if (wrrec(tblock[0]))
! 			goto tape_err;
  	}
***************
*** 100,102
  	}
! 
  	asize += writesize/density;

--- 95,100 -----
  	}
! 	else {
! 		if (write(to, tblock[0], writesize) != writesize)
! 			goto tape_err;
! 	}
  	asize += writesize/density;
***************
*** 109,110
  	timeest();
  }

--- 107,138 -----
  	timeest();
+ 	return;
+ 
+ tape_err:
+ 	if (nogripe)		/* already handling an error, continue */
+ 		return;
+ 	if (pipeout) {
+ 		msg("Tape write error on %s\n", tape);
+ 		msg("Cannot recover\n");
+ 		dumpabort();
+ 		/* NOTREACHED */
+ 	}
+ 	msg("Tape write error on tape %d\n", tapeno);
+ 	broadcast("TAPE ERROR!\n");
+ 	if (query("Do you want to restart?")) {
+ 		msg("This tape will rewind.  After it is rewound,\n");
+ 		msg("replace the faulty tape with a new one;\n");
+ 		msg("this dump volume will be rewritten.\n");
+ 		/*
+ 		 *	Temporarily change the tapeno identification
+ 		 */
+ 		tapeno--;
+ 		nogripe = 1;
+ 		close_rewind();
+ 		nogripe = 0;
+ 		tapeno++;
+ 		Exit(X_REWRITE);
+ 	}
+ 	else
+ 		dumpabort();
+ 	/*NOTREACHED*/
  }
***************
*** 111,112
  
  rewind()

--- 139,143 -----
  
+ /*
+  * Rewind the tape
+  */
  rewind()
***************
*** 125,126
  	 */
  	msg("Tape rewinding\n", secs);

--- 156,159 -----
  	 */
+ 	if (drivfd >= 0)
+ 		t_flusht(0, 1);
  	msg("Tape rewinding\n", secs);
***************
*** 126,127
  	msg("Tape rewinding\n", secs);
  	close(to);

--- 159,164 -----
  	msg("Tape rewinding\n", secs);
+ 	if (drivfd >= 0) {
+ 		close(drivfd);
+ 		drivfd = -1;
+ 	}
  	close(to);
***************
*** 138,139
  		return;
  	close(to);

--- 175,181 -----
  		return;
+ 	if (drivfd >= 0) {
+ 		t_flusht(0, 1);
+ 		close(drivfd);
+ 		drivfd = -1;
+ 	}
  	close(to);
***************
*** 139,141
  	close(to);
! 	if (!nogripe){
  		rewind();

--- 181,183 -----
  	close(to);
! 	if (!nogripe) {
  		rewind();
***************
*** 144,146
  	}
! 	do{
  		if (query ("Is the new tape mounted and ready to go?"))

--- 186,188 -----
  	}
! 	for (;;) {
  		if (query ("Is the new tape mounted and ready to go?"))
***************
*** 147,149
  			break;
! 		if (query ("Do you want to abort?")){
  			dumpabort();

--- 189,191 -----
  			break;
! 		if (query ("Do you want to abort?")) {
  			dumpabort();
***************
*** 151,153
  		}
! 	} while (1);
  }

--- 193,195 -----
  		}
! 	}
  }
***************
*** 177,178
  	 */
  	close(to);

--- 219,225 -----
  	 */
+ 	if (drivfd >= 0) {
+ 		t_flusht(0, 1);
+ 		close(drivfd);
+ 		drivfd = -1;
+ 	}
  	close(to);
***************
*** 186,188
  	childpid = fork();
! 	if (childpid < 0){
  		msg("Context save fork fails in parent %d\n", parentpid);

--- 233,235 -----
  	childpid = fork();
! 	if (childpid < 0) {
  		msg("Context save fork fails in parent %d\n", parentpid);
***************
*** 190,192
  	}
! 	if (childpid != 0){
  		/*

--- 237,239 -----
  	}
! 	if (childpid != 0) {
  		/*
***************
*** 202,204
  #endif TDEBUG
! 		for (;;){
  			waitpid = wait(&status);

--- 249,251 -----
  #endif TDEBUG
! 		for (;;) {
  			waitpid = wait(&status);
***************
*** 204,206
  			waitpid = wait(&status);
! 			if (waitpid != childpid){
  				msg("Parent %d waiting for child %d has another child %d return\n",

--- 251,253 -----
  			waitpid = wait(&status);
! 			if (waitpid != childpid) {
  				msg("Parent %d waiting for child %d has another child %d return\n",
***************
*** 210,212
  		}
! 		if (status & 0xFF){
  			msg("Child %d returns LOB status %o\n",

--- 257,259 -----
  		}
! 		if (status & 0xFF) {
  			msg("Child %d returns LOB status %o\n",
***************
*** 216,218
  #ifdef TDEBUG
! 		switch(status){
  			case X_FINOK:

--- 263,265 -----
  #ifdef TDEBUG
! 		switch (status) {
  			case X_FINOK:
***************
*** 231,233
  #endif TDEBUG
! 		switch(status){
  			case X_FINOK:

--- 278,280 -----
  #endif TDEBUG
! 		switch (status) {
  			case X_FINOK:
***************
*** 249,251
  #endif
! 		do{
  			if (pipeout)

--- 296,298 -----
  #endif
! 		for (;;) {
  			if (pipeout)
***************
*** 254,257
  				to = creat(tape, 0666);
! 			if (to < 0) {
! 				if (!query("Cannot open tape. Do you want to retry the open?"))
  					dumpabort();

--- 301,314 -----
  				to = creat(tape, 0666);
! 			if (to >= 0)
! 				break;
! 			if (!query("Cannot open tape. Do you want to retry the open?")) {
! 				dumpabort();
! 				/*NOTREACHED*/
! 			}
! 		}
! 		if (mass_driver && !pipeout) {
! 			for (;;) {
! 				if (open_driver(to) == 0)
! 					break;
! 				if (!query("Cannot open mass driver. Do you want to retry the open?")) {
  					dumpabort();
***************
*** 257,260
  					dumpabort();
! 			} else break;
! 		} while (1);
  

--- 314,319 -----
  					dumpabort();
! 					/*NOTREACHED*/
! 				}
! 			}
! 		}
  
***************
*** 293,294
  	exit(status);
  }

--- 352,493 -----
  	exit(status);
+ }
+ 
+ /*
+  * Routines for dealing with the mass driver
+  */
+ 
+ /*
+  * Wait for all pending I/O to complete, then check the return
+  * values for each.
+  */
+ wrwait()
+ {
+ 	struct diocret rval;
+ 
+ 	while (ioctl(drivfd, DIOCWAIT, 0)) {
+ 		if (errno == EINTR)
+ 			continue;
+ 		perror("ioctl(DIOCWAIT)");
+ 		msg("Bug! DIOCWAIT fails\n");
+ 		return (-1);
+ 	}
+ 	while (wr_pend > 0) {
+ 		if (ioctl(drivfd, DIOCGETRET, &rval) == 0) {
+ 			wr_pend--;
+ 			if (rval.dioc_error) {
+ 				errno = rval.dioc_error;
+ 				perror("wrwait");
+ 				return (-1);
+ 			}
+ 			if (rval.dioc_rval != writesize)
+ 				return (-1);
+ 			continue;
+ 		}
+ 		if (errno == EINTR)
+ 			continue;
+ 		perror("ioctl(DIOCGETRET)");
+ 		return (-1);
+ 	}
+ 	return (0);
+ }
+ 
+ /*
+  * Write one record, waiting for one previous write to complete
+  * if necessary.
+  */
+ wrrec(addr)
+ 	char *addr;
+ {
+ 	int n, outbits;
+ 	struct diocret rval;
+ 	struct timeval tv;
+ 
+ 	tv.tv_sec = tv.tv_usec = 0;
+ 	while (wr_pend > 0) {
+ 		outbits = 1 << drivfd;
+ 		n = select(drivfd + 1, (int *)0, &outbits, (int *)0,
+ 		    wr_pend >= wr_maxpend ? (struct timeval *)0 : &tv);
+ 		if (n < 0) {
+ 			if (errno == EINTR)
+ 				continue;
+ 			perror("select");
+ 			return (-1);
+ 		}
+ 		if (n == 0) {
+ 			if (wr_pend < wr_maxpend)
+ 				break;
+ 			msg("Bogus zero return from select\n");
+ 			return (-1);
+ 		}
+ 		if (ioctl(drivfd, DIOCGETRET, &rval)) {
+ 			perror("ioctl(DIOCGETRET)");
+ 			return (-1);
+ 		}
+ 		wr_pend--;
+ 		if (rval.dioc_error) {
+ 			errno = rval.dioc_error;
+ 			perror("wrrec");
+ 			return (-1);
+ 		}
+ 		if (rval.dioc_rval != writesize)
+ 			return (-1);
+ 	}
+ 	if (write(drivfd, addr, writesize) != writesize) {
+ 		perror("write");
+ 		return (-1);
+ 	}
+ 	wr_pend++;
+ 	return (0);
+ }
+ 
+ /*
+  * Find and open /dev/drivX, and connect it to the tape (to).
+  * Set wr_maxpend according to the maximum allowable pending operations.
+  */
+ open_driver(to)
+ 	int to;
+ {
+ 	int i;
+ 	char driv_name[40];
+ 	struct stat st;
+ 	struct diocinfo di;
+ 	char *sprintf();
+ 
+ 	for (i = 0;; i++) {
+ 		(void) sprintf(driv_name, "/dev/driv%d", i);
+ 		if ((drivfd = open(driv_name, 2)) >= 0)
+ 			break;
+ 		if (stat(driv_name, &st))
+ 			return (-1);
+ 	}
+ 	if (ioctl (drivfd, DIOCGMAXX, &i)) {
+ 		perror("ioctl(DIOCGMAXX)");
+ 		goto out;
+ 	}
+ 	if (i < 3) {
+ 		msg("This kernel' driver's max_pend is too small\n");
+ 		goto out;
+ 	}
+ 	wr_maxpend = i - 2;	/* safety margin (?) */
+ 	if (WR_MAX && wr_maxpend > WR_MAX)
+ 		wr_maxpend = WR_MAX;
+ 
+ 	di.dioc_fd = to;
+ 	di.dioc_xsize = writesize;
+ again:
+ 	di.dioc_nxfer = wr_maxpend;
+ 	if (ioctl(drivfd, DIOCSETINFO, &di)) {
+ 		if (errno == ENOMEM && wr_maxpend > 1) {
+ 			wr_maxpend--;
+ 			goto again;
+ 		}
+ 		perror("ioctl(DIOCSETINFO)");
+ 		goto out;
+ 	}
+ 	msg("[wr_maxpend set to %d]\n", wr_maxpend);/* debug */
+ 	return (0);
+ 
+ out:
+ 	close(drivfd);
+ 	return (-1);
  }
RCS file: RCS/dumptraverse.c,v
retrieving revision 1.1
diff -c1 -r1.1 dumptraverse.c
*** /tmp/,RCSt1005046	Sat Jan  5 03:23:21 1985
--- dumptraverse.c	Mon Dec 31 23:06:24 1984
***************
*** 5,8
  pass(fn, map)
! 	int (*fn)();
! 	char *map;
  {

--- 5,8 -----
  pass(fn, map)
! 	register int (*fn)();
! 	register char *map;
  {
***************
*** 8,12
  {
! 	struct dinode *dp;
! 	int bits;
! 	ino_t maxino;
  

--- 8,11 -----
  {
! 	register int bits;
! 	register ino_t maxino;
  
***************
*** 14,16
  	for (ino = 0; ino < maxino; ) {
! 		if((ino % NBBY) == 0) {
  			bits = ~0;

--- 13,19 -----
  	for (ino = 0; ino < maxino; ) {
! #if NBBY == 8
! 		if ((ino & 7) == 0) {
! #else
! 		if ((ino % NBBY) == 0) {
! #endif
  			bits = ~0;
***************
*** 16,18
  			bits = ~0;
! 			if(map != NULL)
  				bits = *map++;

--- 19,21 -----
  			bits = ~0;
! 			if (map != NULL)
  				bits = *map++;
***************
*** 20,25
  		ino++;
! 		if(bits & 1) {
! 			dp = getino(ino);
! 			(*fn)(dp);
! 		}
  		bits >>= 1;

--- 23,26 -----
  		ino++;
! 		if (bits & 1)
! 			(*fn)(getino(ino));
  		bits >>= 1;
***************
*** 31,33
  {
! 	register f;
  

--- 32,34 -----
  {
! 	register int f;
  
***************
*** 34,36
  	f = ip->di_mode & IFMT;
! 	if(f == 0)
  		return;

--- 35,37 -----
  	f = ip->di_mode & IFMT;
! 	if (f == 0)
  		return;
***************
*** 37,39
  	BIS(ino, clrmap);
! 	if(f == IFDIR)
  		BIS(ino, dirmap);

--- 38,40 -----
  	BIS(ino, clrmap);
! 	if (f == IFDIR)
  		BIS(ino, dirmap);
***************
*** 51,53
  add(ip)
! 	register struct	dinode	*ip;
  {

--- 52,54 -----
  add(ip)
! 	register struct	dinode *ip;
  {
***************
*** 56,58
  
! 	if(BIT(ino, nodmap))
  		return;

--- 57,59 -----
  
! 	if (BIT(ino, nodmap))
  		return;
***************
*** 70,72
  	}
! 	if(dadded) {
  		nadded++;

--- 71,73 -----
  	}
! 	if (dadded) {
  		nadded++;
***************
*** 77,80
  	}
! 	if(nsubdir == 0)
! 		if(!BIT(ino, nodmap))
  			BIC(ino, dirmap);

--- 78,81 -----
  	}
! 	if (nsubdir == 0)
! 		if (!BIT(ino, nodmap))
  			BIC(ino, dirmap);
***************
*** 86,88
  {
! 	register i;
  	daddr_t	idblk[MAXNINDIR];

--- 87,89 -----
  {
! 	register int i;
  	daddr_t	idblk[MAXNINDIR];
***************
*** 90,93
  	bread(fsbtodb(sblock, d), (char *)idblk, sblock->fs_bsize);
! 	if(n <= 0) {
! 		for(i=0; i < NINDIR(sblock); i++) {
  			d = idblk[i];

--- 91,94 -----
  	bread(fsbtodb(sblock, d), (char *)idblk, sblock->fs_bsize);
! 	if (n <= 0) {
! 		for (i=0; i < NINDIR(sblock); i++) {
  			d = idblk[i];
***************
*** 93,95
  			d = idblk[i];
! 			if(d != 0)
  				dsrch(d, sblock->fs_bsize, *filesize);

--- 94,96 -----
  			d = idblk[i];
! 			if (d != 0)
  				dsrch(d, sblock->fs_bsize, *filesize);
***************
*** 99,101
  		n--;
! 		for(i=0; i < NINDIR(sblock); i++) {
  			d = idblk[i];

--- 100,102 -----
  		n--;
! 		for (i=0; i < NINDIR(sblock); i++) {
  			d = idblk[i];
***************
*** 101,103
  			d = idblk[i];
! 			if(d != 0)
  				indir(d, n, filesize);

--- 102,104 -----
  			d = idblk[i];
! 			if (d != 0)
  				indir(d, n, filesize);
***************
*** 107,109
  
! dump(ip)
  	struct dinode *ip;

--- 108,116 -----
  
! /*
!  * Like dump, but checks for inodes that aren't directories.  This is
!  * really not right, but prevents restore from believing that this inode
!  * entry (which has changed into a file while we were doing other stuff)
!  * is the end of the list of directories.
!  */
! dirdump(ip)
  	struct dinode *ip;
***************
*** 110,111
  {
  	register int i;

--- 117,129 -----
  {
+ 	if ((ip->di_mode & IFMT) != IFDIR) {
+ 		msg("WARNING: active file system; directory vanished\n");
+ 		msg("(you aren't supposed to dump active file systems.)\n");
+ 		return;
+ 	}
+ 	dump(ip);
+ }
+ 
+ dump(ip)
+ 	register struct dinode *ip;
+ {
  	register int i;
***************
*** 113,115
  
! 	if(newtape) {
  		newtape = 0;

--- 131,133 -----
  
! 	if (newtape) {
  		newtape = 0;
***************
*** 122,123
  	i = ip->di_mode & IFMT;
  	if ((i != IFDIR && i != IFREG && i != IFLNK) || ip->di_size == 0) {

--- 140,146 -----
  	i = ip->di_mode & IFMT;
+ 	if (i == 0) {		/* free inode */
+ 		msg("WARNING: active file system; file vanished\n");
+ 		msg("(you aren't supposed to dump active file systems.)\n");
+ 		return;
+ 	}
  	if ((i != IFDIR && i != IFREG && i != IFLNK) || ip->di_size == 0) {
***************
*** 171,173
  blksout(blkp, frags)
! 	daddr_t *blkp;
  	int frags;

--- 194,196 -----
  blksout(blkp, frags)
! 	register daddr_t *blkp;
  	int frags;
***************
*** 174,176
  {
! 	int i, j, count, blks, tbperdb;
  

--- 197,199 -----
  {
! 	register int i, j, count, blks, tbperdb;
  
***************
*** 205,208
  {
! 	register i, n;
! 	char *cp;
  

--- 228,231 -----
  {
! 	register int i;
! 	register char *cp;
  
***************
*** 208,216
  
- 	n = -1;
- 	for (i = 0; i < msiz; i++)
- 		if(map[i])
- 			n = i;
- 	if (n < 0)
- 		return;
- 	n++;
  	spcl.c_type = typ;

--- 231,232 -----
  
  	spcl.c_type = typ;
***************
*** 216,218
  	spcl.c_type = typ;
! 	spcl.c_count = howmany(n * sizeof(map[0]), TP_BSIZE);
  	spclrec();

--- 232,234 -----
  	spcl.c_type = typ;
! 	spcl.c_count = howmany(msiz * sizeof(map[0]), TP_BSIZE);
  	spclrec();
***************
*** 231,233
  	s = 0;
! 	for(i = 0; i < sizeof(union u_spcl)/sizeof(int); i++)
  		s += *ip++;

--- 247,249 -----
  	s = 0;
! 	for (i = 0; i < sizeof(union u_spcl)/sizeof(int); i++)
  		s += *ip++;
***************
*** 239,241
  	daddr_t d;
! 	int size, filesize;
  {

--- 255,258 -----
  	daddr_t d;
! 	int size;
! 	register int filesize;
  {
***************
*** 242,244
  	register struct direct *dp;
! 	long loc;
  	char dblk[MAXBSIZE];

--- 259,261 -----
  	register struct direct *dp;
! 	register long loc;
  	char dblk[MAXBSIZE];
***************
*** 245,247
  
! 	if(dadded)
  		return;

--- 262,264 -----
  
! 	if (dadded)
  		return;
***************
*** 257,259
  		loc += dp->d_reclen;
! 		if(dp->d_ino == 0)
  			continue;

--- 274,276 -----
  		loc += dp->d_reclen;
! 		if (dp->d_ino == 0)
  			continue;
***************
*** 259,262
  			continue;
! 		if(dp->d_name[0] == '.') {
! 			if(dp->d_name[1] == '\0')
  				continue;

--- 276,279 -----
  			continue;
! 		if (dp->d_name[0] == '.') {
! 			if (dp->d_name[1] == '\0')
  				continue;
***************
*** 262,264
  				continue;
! 			if(dp->d_name[1] == '.' && dp->d_name[2] == '\0')
  				continue;

--- 279,281 -----
  				continue;
! 			if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
  				continue;
***************
*** 265,267
  		}
! 		if(BIT(dp->d_ino, nodmap)) {
  			dadded++;

--- 282,284 -----
  		}
! 		if (BIT(dp->d_ino, nodmap)) {
  			dadded++;
***************
*** 269,271
  		}
! 		if(BIT(dp->d_ino, dirmap))
  			nsubdir++;

--- 286,288 -----
  		}
! 		if (BIT(dp->d_ino, dirmap))
  			nsubdir++;
***************
*** 296,298
  	char *ba;
! 	int	cnt;	
  {

--- 313,315 -----
  	char *ba;
! 	int cnt;
  {
***************
*** 301,303
  loop:
! 	if (lseek(fi, (long)(da * DEV_BSIZE), 0) < 0){
  		msg("bread: lseek fails\n");

--- 318,320 -----
  loop:
! 	if (lseek(fi, (long)(da * DEV_BSIZE), 0) < 0)
  		msg("bread: lseek fails\n");
***************
*** 303,305
  		msg("bread: lseek fails\n");
- 	}
  	n = read(fi, ba, cnt);

--- 320,321 -----
  		msg("bread: lseek fails\n");
  	n = read(fi, ba, cnt);
***************
*** 311,313
  		 *
! 		 * NB - dump only works in TP_BSIZE blocks, hence
  		 * rounds DEV_BSIZE fragments up to TP_BSIZE pieces.

--- 327,329 -----
  		 *
! 		 * N.B. - dump only works in TP_BSIZE blocks, hence
  		 * rounds DEV_BSIZE fragments up to TP_BSIZE pieces.
***************
*** 323,325
  		disk, da, cnt, n);
! 	if (++breaderrors > BREADEMAX){
  		msg("More than %d block read errors from %d\n",

--- 339,341 -----
  		disk, da, cnt, n);
! 	if (++breaderrors > BREADEMAX) {
  		msg("More than %d block read errors from %d\n",
***************
*** 328,330
  		msg("This is an unrecoverable error.\n");
! 		if (!query("Do you want to attempt to continue?")){
  			dumpabort();

--- 344,346 -----
  		msg("This is an unrecoverable error.\n");
! 		if (!query("Do you want to attempt to continue?")) {
  			dumpabort();
***************
*** 331,333
  			/*NOTREACHED*/
! 		} else
  			breaderrors = 0;

--- 347,350 -----
  			/*NOTREACHED*/
! 		}
! 		else
  			breaderrors = 0;
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 dump_mods
	/bin/echo -n '	'; /bin/ls -ld dump_mods
fi
/bin/echo 'Extracting md.4'
sed 's/^X//' <<'//go.sysin dd *' >md.4
X.TH DRIV 4L "U of Maryland"
X.UC 4
X.SH NAME
driv \- ``mass driver'' asynchronous device
X.SH SYNOPSIS
X.B "pseudo-device driv"
X.br
or
X.br
X.B "pseudo-device driv"
X.I N
X.SH DESCRIPTION
\fI/dev/driv0\fP \- \fI/dev/driv\fP\|(N-1)
are special files that allow asynchronous raw I/O on other devices.  They
work by stealing memory from the system buffer pool, reserving it for
the user of the pseudo driver for asynchronous transfers.  This driver
does nothing by itself; it
X.I must
be used in conjunction with another device.  Due to system limitations,
it generally operates only with mass storage devices (tapes and disks).
X.PP
The mass driver is controlled by means of
X.I ioctl
commands defined in the file
X.RI < sys/drivio.h >,
as follows:
X.PP
X.nf
X/* $Header: /usr/sys/h/RCS/drivio.h,v 1.1 85/01/02 09:44:05 chris Exp $ */

X/*
 * diocinfo contains the information needed to initialize the mass driver
 */
struct diocinfo {
	int	dioc_fd;	/* file descriptor */
	int	dioc_xsize;	/* max size of any one transfer */
	int	dioc_nxfer;	/* max number of uncompleted transfers */
};

X/*
 * diocret contains the return status information for completed transfers
 */
struct diocret {
	int	dioc_rval;	/* return value */
	int	dioc_error;	/* error number */
};

#define DIOCSETINFO _IOW(D, 0, struct diocinfo)	/* set info */
#define DIOCREAD    _IO(D, 1)			/* read (NOT DONE YET) */
#define DIOCGETRET  _IOR(D, 2, struct diocret)	/* get return info */
#define DIOCWAIT    _IO(D, 3)			/* wait for I/O to finish */
#define DIOCGMAXX   _IOR(D, 4, int)		/* get max # xfers */
X.PP
X.fi
X.PP
X.B DIOCSETINFO
is used to initialize the mass driver.  It connects the mass driver to
the file opened as
X.IR dioc_fd ,
which must be a character (raw) device (most likely a tape drive) and
must support the mass driver.
At the same time, space for holding transfers is allocated based on
X.I dioc_xsize
and
X.IR dioc_nxfer .
The first is the maximum size allowed in
X.I read
or
X.I write
system calls on the mass driver.  (Note that the raw device may
be used to perform larger transfers, if necessary.)  There is an
absolute limit of
X.B MAXBSIZE
bytes, which can never be exceeded (short of recompiling the kernel).
X.RB ( MAXBSIZE
may be found in the file
X.RI < sys/param.h >).
There is also an implementation-dependent limit on the number of transfers
X.IR dioc_nxfer ;
this can be found
X.RI ( before
the mass driver is connected) via the
X.B DIOCGMAXX
ioctl.  (Once the driver is successfully connected,
X.B DIOCGMAXX
will return the value given as
X.IR dioc_nxfer .)
X.PP
Once the mass driver has been successfully connected to the I/O device,
asynchronous writes can be initiated with the
X.I write
system call.  When a write occurs, the driver first waits if necessary
so that no more than
X.I dioc_nxfer
asynchronous transfers are still pending, then starts but does not wait
for the given transfer.  When the
X.I write
system call returns, the data has been copied someplace safe, so the
buffer passed to
X.I write
may be immediately reused.
X.PP
X.IR Write s
on the mass driver always succeed immediately (assuming they are not
too large), moving the mass driver file's seek pointer (but not that
of the true device; this is important on devices that use the file
offset, e.g., disk drives).  Return values are computed, and errors
are detected, after the mass driver write has succeeded.  These values
should be retrieved with the
X.B DIOCGETRET
ioctl and examined.  The
X.I select
system call will succeed for writing whenever a return value
is available (yes, it's odd, but what the heck).  The value that
X.I would
have been returned from a
X.I write
on the raw device is given back in
X.IR dioc_rval ,
with
X.I dioc_error
containing the system error number, if any (see
X.RI < errno.h >),
or zero for no error.  (Note that it is possible to have a nonzero
return value and an error, if a transfer succeeds partially.)
Transfers are always done in order, thus the return value applies to
the oldest write for which no value has been returned yet.
X.PP
The
X.B DIOCWAIT
command waits for all pending transfers to complete, after which their
return values may be obtained.
X.PP
Currently,
X.B DIOCREAD
and read transfers are not supported (sorry!).  The current idea for
these is to have
X.B DIOCREAD
take a transfer size (and an offset?) and start a transfer,
with select for read returning when the read is complete.
X.SH ERRORS
There are a large number of error codes that can be returned, some
probably inappropriate.
The following errors can be returned by the mass driver (and are
additional to errors from the standard system call interface).
X.PP
X.TP 15
[ENOENT]
A
X.B DIOCGETRET
ioctl was attempted but there were no return values.
X.TP 15
[EBADF]
Either of the following:
X.br
\(bu The
X.I dioc_fd
given to
X.B DIOCSETINFO
did not refer to an open file.
X.br
\(bu A
X.I write
was attempted when the true device was only opened for reading.
X.TP 15
[ENOMEM]
Either of the following:
X.br
\(bu Not enough buffer space was available to allocate
X.I dioc_nxfer
transfer buffers of size
X.IR dioc_xsize .
X.br
\(bu A
X.I write
of more than
X.I dioc_xsize
bytes was attempted.
X.TP 15
[EBUSY]
The driver is in use by another program.
X.TP 15
[EINVAL]
Any of the following:
X.br
\(bu A
X.B DIOCSETINFO
command was done but the mass driver was already connected.
X.br
\(bu The
X.I dioc_nxfer
or
X.I dioc_xsize
value given to
X.B DIOCSETINFO
was negative.
X.br
\(bu The
X.I dioc_fd
file descriptor did not refer to a character special device, or the
device does not support the mass driver.
X.br
\(bu A
X.I write
was attempted without connecting the mass driver to a real device.
X.TP 15
[ENOTTY]
An unsupported ioctl was attempted.
X.TP 15
[ENOSPC]
The value of
X.I dioc_xsize
given to
X.B DIOCSETINFO
was greater than
X.BR MAXBSIZE .
X.TP 15
[EOPNOTSUPP]
A
X.I read
or
X.B DIOCREAD
was attempted.  This is probably the wrong error number.
It may change to ENXIO (or it may go away (wish wish)).
X.SH AUTHOR
Chris Torek, University of Maryland
X.br
<chris@maryland.ARPA>
X.br
<chris@umcp-cs.UUCP>
X.SH FILES
X/dev/driv*
X.SH BUGS
Reads should be supported.
X.PP
Half of the buffer cache is reserved for the normal block I/O system
and cannot be used by the mass driver.  However, it is still possible
for a malicious user to steal half the system buffer space, adversely
affecting system performance.  (So restrict the /dev entry, silly!)
X.PP
The method of obtaining return values is ugly.
X.PP
The effect of select is odd.
X.PP
While the mass driver is open, the ``true'' (raw) device is held open.  The
correct procedure for shutting things down is to close the mass
driver, then the raw device.  Simply closing a raw tape drive will
not cause it to rewind, for example.
X.PP
The error returns are weird.
X.PP
There should be ioctls to get the current number of pending transfers
and valid return values and any other interesting goodies.
X.PP
Errors are not handled gracefully.  If an error occurs on the true
device, the mass driver barrels on ahead with the rest of the pending
transfers.  Probably, it should suspend them, allowing a later ioctl
to restart or cancel the pending transfers.
X.PP
The
X.B MAXBSIZE
limit is annoying.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 md.4
	/bin/echo -n '	'; /bin/ls -ld md.4
fi
/bin/echo 'Extracting needless_conf.h'
sed 's/^X//' <<'//go.sysin dd *' >needless_conf.h
Here is a PARTIAL list of files that needlessly include ../h/conf.h:

sys/kern_descrip.c
sys/kern_prot.c
sys/kern_sig.c
sys/subr_prf.c
sys/tty_bk.c
sys/tty_subr.c
sys/tty_tb.c
sys/ufs_alloc.c
sys/ufs_bmap.c
sys/ufs_fio.c
sys/ufs_inode.c
sys/ufs_nami.c
sys/ufs_subr.c
sys/ufs_xxx.c
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 needless_conf.h
	/bin/echo -n '	'; /bin/ls -ld needless_conf.h
fi
-- 
(This line accidently left nonblank.)

In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (301) 454-7690
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@maryland

chris@umcp-cs.UUCP (Chris Torek) (01/05/85)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting kernel_mods'
sed 's/^X//' <<'//go.sysin dd *' >kernel_mods
WARNING: You can't just apply these diffs.  There are comments next
to each to tell you why it's there.  Some of them are parts of other
fixes, some are local devices; anyway, you have to read the ``comments''.

Be sure to read the README first!

RCS file: RCS/conf.c,v
retrieving revision 1.1
diff -b -c1 -r1.1 conf.c
*** /tmp/,RCSt1003142	Fri Jan  4 23:00:18 1985
--- conf.c	Thu Jan  3 11:57:30 1985
*************** I think this one is obvious.
*** 14,16
  int	hpopen(),hpstrategy(),hpread(),hpwrite(),hpdump(),hpioctl(),hpsize();
! #else
  #define	hpopen		nodev

--- 14,16 -----
  int	hpopen(),hpstrategy(),hpread(),hpwrite(),hpdump(),hpioctl(),hpsize();
! #else NHP > 0
  #define	hpopen		nodev
*************** Likewise...
*** 22,24
  #define	hpsize		0
! #endif
   

--- 22,24 -----
  #define	hpsize		0
! #endif NHP > 0
   
*************** Here's an addition for the mass driver on TU77s.
*** 27,28
  int	htopen(),htclose(),htstrategy(),htread(),htwrite(),htdump(),htioctl();
  #else

--- 27,29 -----
  int	htopen(),htclose(),htstrategy(),htread(),htwrite(),htdump(),htioctl();
+ int	htmass();
  #else
*************** Again, but if you have no TU77s.
*** 35,36
  #define	htioctl		nodev
  #endif

--- 36,38 -----
  #define	htioctl		nodev
+ #define	htmass		0
  #endif
*************** Not sure what this is; something used at BRL.
*************** You probably don't want it.
*** 37,38
  
  #include "rk.h"

--- 39,51 -----
  
+ #include "rf.h"
+ #if NXF > 0
+ int	rfopen(),rfstrategy(),rfread(),rfwrite(),rfreset();
+ #else
+ #define	rfopen		nodev
+ #define rfstrategy	nodev
+ #define rfread		nodev
+ #define rfwrite		nodev
+ #define rfreset		nodev
+ #endif
+ 
  #include "rk.h"
*************** More mass driver stuff, this for the TS11.
*** 70,72
  int	tsopen(),tsclose(),tsstrategy(),tsread(),tswrite();
! int	tsioctl(),tsdump(),tsreset();
  #else

--- 83,85 -----
  int	tsopen(),tsclose(),tsstrategy(),tsread(),tswrite();
! int	tsioctl(),tsdump(),tsreset(),tsmass();
  #else
*************** Same, but if you have no TS11s.
*** 80,81
  #define	tsreset		nodev
  #endif

--- 93,95 -----
  #define	tsreset		nodev
+ #define	tsmass		0
  #endif
*************** Still more mass driver additions, for the TU78.
*** 85,87
  int	mtopen(),mtclose(),mtstrategy(),mtread(),mtwrite();
! int	mtioctl(),mtdump();
  #else

--- 99,101 -----
  int	mtopen(),mtclose(),mtstrategy(),mtread(),mtwrite();
! int	mtioctl(),mtdump(),mtmass();
  #else
*************** Mass driver stuff for when you have no TU78s.
*** 94,95
  #define	mtdump		nodev
  #endif

--- 108,110 -----
  #define	mtdump		nodev
+ #define	mtmass		0
  #endif
*************** The BRL rf disk, which you probably don't want.
*** 231,232
  	  rlsize,	0 },
  };

--- 246,249 -----
  	  rlsize,	0 },
+ 	{ rfopen,	nulldev,	rfstrategy,	nodev,		/*15*/
+ 	  0,		0 },
  };
*************** The dh ``outgoing mode'' mods, which I posted before.
*************** If you didn't get them then, you don't want this now.
*** 263,264
  #define	dh11	0
  #else

--- 280,285 -----
  #define	dh11	0
+ #define dhoopen nodev
+ #define dhoclose nodev
+ #define dhpopen nodev
+ #define dhpclose nodev
  #else
*************** Continuation of above...
*** 266,267
  struct	tty dh11[];
  #endif

--- 287,294 -----
  struct	tty dh11[];
+ #include "dho.h"
+ #if NDHO > 0
+ int	dhoopen(),dhoclose();
+ #else
+ #define dhoopen nodev
+ #define dhoclose nodev
  #endif
*************** Passive mode DH (doesn't quite work anyway; you don't want it)
*** 267,268
  #endif
  

--- 294,303 -----
  #endif
+ #include "dhp.h"
+ #if NDHP > 0
+ int	dhpopen(),dhpclose();
+ #else
+ #define dhpopen nodev
+ #define dhpclose nodev
+ #endif
+ #endif
  
*************** 1. The ``file descriptor'' pseudo-device.  If you didn't
***************    get a copy earlier, ignore these too.
*************** 2. The ``mass driver'' pseudo-device (from #include "driv.h"
***************    on.  Now this, you want....
*** 268,269
  
  #include "dmf.h"

--- 303,324 -----
  
+ #include "fd.h"
+ #if NFD > 0
+ int	fdopen();
+ #else
+ #define	fdopen	nodev
+ #endif NFD
+ 
+ #include "driv.h"
+ #if NDRIV > 0
+ int	drivopen(), drivclose(), drivread(), drivwrite(), drivioctl();
+ int	drivselect();
+ #else NDRIV > 0
+ #define drivopen	nodev
+ #define drivclose	nodev
+ #define drivread	nodev
+ #define drivwrite	nodev
+ #define drivioctl	nodev
+ #define drivselect	nodev
+ #endif NDRIV > 0
+ 
  #include "dmf.h"
*************** DZ outgoing mods.  Ignore these if you haven't got them
*************** already...
*** 302,303
  #define	dz_tty	0
  #else

--- 357,360 -----
  #define	dz_tty	0
+ #define dzoopen nodev
+ #define dzoclose nodev
  #else
*************** etc...
*** 305,306
  struct	tty dz_tty[];
  #endif

--- 362,369 -----
  struct	tty dz_tty[];
+ #include "dzo.h"
+ #if NDZO > 0
+ int	dzoopen(),dzoclose();
+ #else
+ #define dzoopen nodev
+ #define dzoclose nodev
  #endif
*************** etc...
*** 306,307
  #endif
  

--- 369,371 -----
  #endif
+ #endif
  
*************** Another BRL addition, the Ikonas frame buffer.  Ignore...
*** 411,412
  
  #include "ps.h"

--- 475,488 -----
  
+ #include "ik.h"
+ #if NIK > 0
+ int	ikopen(),ikclose(),ikread(),ikwrite(),ikioctl(),ikreset();
+ #else
+ #define ikopen nodev
+ #define ikclose nodev
+ #define ikread nodev
+ #define ikwrite nodev
+ #define ikioctl nodev
+ #define ikreset nodev
+ #endif
+ 
  #include "ps.h"
*************** Here come a bunch of BRL and U of MD local devices,
*************** which you also don't want.
*** 445,446
  
  int	ttselect(), seltrue();

--- 521,616 -----
  
+ #include "mg.h"				/* BRL */
+ #if NMG > 0
+ int	mgopen(),mgclose(),mgreset(),mgread(),mgwrite(),mgioctl(),mgselect();
+ #else
+ #define mgopen nodev
+ #define mgclose nodev
+ #define mgreset nodev
+ #define mgread nodev
+ #define mgwrite nodev
+ #define mgioctl nodev
+ #define mgselect nodev
+ #endif
+ 
+ #include "vg.h"				/* BRL */
+ #if NVG > 0
+ int	vgopen(),vgclose(),vgreset(),vgread(),vgwrite(),vgioctl(),vgselect();
+ #else
+ #define vgopen nodev
+ #define vgclose nodev
+ #define vgreset nodev
+ #define vgread nodev
+ #define vgwrite nodev
+ #define vgioctl nodev
+ #define vgselect nodev
+ #endif
+ 
+ #include "ump.h"				/* BRL */
+ #if NUMP > 0
+ int	umpopen(),umpclose(),umpread(),umpwrite(),umpioctl(),umpselect();
+ #else
+ #define umpopen nodev
+ #define umpclose nodev
+ #define umpread nodev
+ #define umpwrite nodev
+ #define umpioctl nodev
+ #define umpselect nodev
+ #endif
+ 
+ #include "hsw.h"				/* BRL */
+ #if NHSW > 0
+ int	hswopen(),hswclose(),hswread(),hswwrite(),hswselect();
+ #else
+ #define hswopen nodev
+ #define hswclose nodev
+ #define hswread nodev
+ #define hswwrite nodev
+ #define hswselect nodev
+ #endif
+ 
+ #include "lsb.h"				/* BRL */
+ #if NLSB > 0
+ int	lsbopen(),lsbclose(),lsbread(),lsbwrite(),lsbreset();
+ #else
+ #define lsbopen nodev
+ #define lsbclose nodev
+ #define lsbread nodev
+ #define lsbwrite nodev
+ #define lsbreset nodev
+ #endif
+ 
+ #include "mcd.h"				/* BRL */
+ #if NMCD > 0
+ int	mcdopen(),mcdclose(),mcdread(),mcdwrite(),mcdreset();
+ #else
+ #define mcdopen nodev
+ #define mcdclose nodev
+ #define mcdread nodev
+ #define mcdwrite nodev
+ #define mcdreset nulldev
+ #endif
+ 
+ #include "mss.h"				/* BRL */
+ #if NMSS > 0
+ int	mssopen(),mssclose(),mssselect(),mssreset(), mssioctl();
+ #else
+ #define	mssopen	nodev
+ #define	mssclose nodev
+ #define	mssselect nodev
+ #define	mssreset nodev
+ #define mssioctl nodev
+ #endif
+ 
+ /* zmob pseudo-device multiplexor FLB */
+ #include "zm.h"
+ #if NZM > 0
+ int   zmopen(),   zmclose(),   zmread(),   zmwrite(),   zmioctl();
+ #else
+ #define    zmopen nodev
+ #define   zmclose nodev
+ #define    zmread nodev
+ #define   zmwrite nodev
+ #define   zmioctl nodev
+ #endif
+ 
  int	ttselect(), seltrue();
*************** Here come changes to the cdevsw[] table, as described
*************** in the installation instructions.  You need to make
*************** the addition of ``0, 0,'' to each existing device in
*************** your own table (except of course for those devices with
*************** mass driver entries).
*** 451,453
  	cnioctl,	nulldev,	nulldev,	&cons,
! 	ttselect,	nodev,
  	dzopen,		dzclose,	dzread,		dzwrite,	/*1*/

--- 621,624 -----
  	cnioctl,	nulldev,	nulldev,	&cons,
! 	ttselect,	nodev,		0,		0,
! 
  	dzopen,		dzclose,	dzread,		dzwrite,	/*1*/
***************
*** 454,456
  	dzioctl,	dzstop,		dzreset,	dz_tty,
! 	ttselect,	nodev,
  	syopen,		nulldev,	syread,		sywrite,	/*2*/

--- 625,628 -----
  	dzioctl,	dzstop,		dzreset,	dz_tty,
! 	ttselect,	nodev,		0,		0,
! 
  	syopen,		nulldev,	syread,		sywrite,	/*2*/
***************
*** 457,459
  	syioctl,	nulldev,	nulldev,	0,
! 	syselect,	nodev,
  	nulldev,	nulldev,	mmread,		mmwrite,	/*3*/

--- 629,632 -----
  	syioctl,	nulldev,	nulldev,	0,
! 	syselect,	nodev,		0,		0,
! 
  	nulldev,	nulldev,	mmread,		mmwrite,	/*3*/
***************
*** 460,462
  	nodev,		nulldev,	nulldev,	0,
! 	mmselect,	nodev,
  	hpopen,		nulldev,	hpread,		hpwrite,	/*4*/

--- 633,636 -----
  	nodev,		nulldev,	nulldev,	0,
! 	mmselect,	nodev,		0,		0,
! 
  	hpopen,		nulldev,	hpread,		hpwrite,	/*4*/
***************
*** 463,465
  	hpioctl,	nodev,		nulldev,	0,
! 	seltrue,	nodev,
  	htopen,		htclose,	htread,		htwrite,	/*5*/

--- 637,640 -----
  	hpioctl,	nodev,		nulldev,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	htopen,		htclose,	htread,		htwrite,	/*5*/
***************
*** 466,468
  	htioctl,	nodev,		nulldev,	0,
! 	seltrue,	nodev,
  	vpopen,		vpclose,	nodev,		vpwrite,	/*6*/

--- 641,644 -----
  	htioctl,	nodev,		nulldev,	0,
! 	seltrue,	nodev,		htmass,		htstrategy,
! 
  	vpopen,		vpclose,	nodev,		vpwrite,	/*6*/
***************
*** 469,471
  	vpioctl,	nulldev,	vpreset,	0,
! 	vpselect,	nodev,
  	nulldev,	nulldev,	swread,		swwrite,	/*7*/

--- 645,648 -----
  	vpioctl,	nulldev,	vpreset,	0,
! 	vpselect,	nodev,		0,		0,
! 
  	nulldev,	nulldev,	swread,		swwrite,	/*7*/
***************
*** 472,474
  	nodev,		nodev,		nulldev,	0,
! 	nodev,		nodev,
  	flopen,		flclose,	flread,		flwrite,	/*8*/

--- 649,652 -----
  	nodev,		nodev,		nulldev,	0,
! 	nodev,		nodev,		0,		0,
! 
  	flopen,		flclose,	flread,		flwrite,	/*8*/
***************
*** 475,477
  	nodev,		nodev,		nulldev,	0,
! 	seltrue,	nodev,
  	udopen,		nulldev,	udread,		udwrite,	/*9*/

--- 653,656 -----
  	nodev,		nodev,		nulldev,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	udopen,		nulldev,	udread,		udwrite,	/*9*/
***************
*** 478,480
  	nodev,		nodev,		udreset,		0,
! 	seltrue,	nodev,
  	vaopen,		vaclose,	nodev,		vawrite,	/*10*/

--- 657,660 -----
  	nodev,		nodev,		udreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	vaopen,		vaclose,	nodev,		vawrite,	/*10*/
***************
*** 481,483
  	vaioctl,	nulldev,	vareset,	0,
! 	vaselect,	nodev,
  	rkopen,		nulldev,	rkread,		rkwrite,	/*11*/

--- 661,664 -----
  	vaioctl,	nulldev,	vareset,	0,
! 	vaselect,	nodev,		0,		0,
! 
  	rkopen,		nulldev,	rkread,		rkwrite,	/*11*/
***************
*** 484,486
  	nodev,		nodev,		rkreset,	0,
! 	seltrue,	nodev,
  	dhopen,		dhclose,	dhread,		dhwrite,	/*12*/

--- 665,668 -----
  	nodev,		nodev,		rkreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	dhopen,		dhclose,	dhread,		dhwrite,	/*12*/
***************
*** 487,489
  	dhioctl,	dhstop,		dhreset,	dh11,
! 	ttselect,	nodev,
  	upopen,		nulldev,	upread,		upwrite,	/*13*/

--- 669,672 -----
  	dhioctl,	dhstop,		dhreset,	dh11,
! 	ttselect,	nodev,		0,		0,
! 
  	upopen,		nulldev,	upread,		upwrite,	/*13*/
***************
*** 490,492
  	nodev,		nodev,		upreset,	0,
! 	seltrue,	nodev,
  	tmopen,		tmclose,	tmread,		tmwrite,	/*14*/

--- 673,676 -----
  	nodev,		nodev,		upreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	tmopen,		tmclose,	tmread,		tmwrite,	/*14*/
***************
*** 493,495
  	tmioctl,	nodev,		tmreset,	0,
! 	seltrue,	nodev,
  	lpopen,		lpclose,	nodev,		lpwrite,	/*15*/

--- 677,680 -----
  	tmioctl,	nodev,		tmreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	lpopen,		lpclose,	nodev,		lpwrite,	/*15*/
***************
*** 496,498
  	nodev,		nodev,		lpreset,	0,
! 	seltrue,	nodev,
  	tsopen,		tsclose,	tsread,		tswrite,	/*16*/

--- 681,684 -----
  	nodev,		nodev,		lpreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	tsopen,		tsclose,	tsread,		tswrite,	/*16*/
***************
*** 499,501
  	tsioctl,	nodev,		tsreset,	0,
! 	seltrue,	nodev,
  	utopen,		utclose,	utread,		utwrite,	/*17*/

--- 685,688 -----
  	tsioctl,	nodev,		tsreset,	0,
! 	seltrue,	nodev,		tsmass,		tsstrategy,
! 
  	utopen,		utclose,	utread,		utwrite,	/*17*/
***************
*** 502,504
  	utioctl,	nodev,		utreset,	0,
! 	seltrue,	nodev,
  	ctopen,		ctclose,	nodev,		ctwrite,	/*18*/

--- 689,692 -----
  	utioctl,	nodev,		utreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	ctopen,		ctclose,	nodev,		ctwrite,	/*18*/
***************
*** 505,507
  	nodev,		nodev,		nulldev,	0,
! 	seltrue,	nodev,
  	mtopen,		mtclose,	mtread,		mtwrite,	/*19*/

--- 693,696 -----
  	nodev,		nodev,		nulldev,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	mtopen,		mtclose,	mtread,		mtwrite,	/*19*/
***************
*** 508,510
  	mtioctl,	nodev,		nodev,		0,
! 	seltrue,	nodev,
  	ptsopen,	ptsclose,	ptsread,	ptswrite,	/*20*/

--- 697,700 -----
  	mtioctl,	nodev,		nodev,		0,
! 	seltrue,	nodev,		mtmass,		mtstrategy,
! 
  	ptsopen,	ptsclose,	ptsread,	ptswrite,	/*20*/
***************
*** 511,513
  	ptyioctl,	ptsstop,	nodev,		pt_tty,
! 	ttselect,	nodev,
  	ptcopen,	ptcclose,	ptcread,	ptcwrite,	/*21*/

--- 701,704 -----
  	ptyioctl,	ptsstop,	nodev,		pt_tty,
! 	ttselect,	nodev,		0,		0,
! 
  	ptcopen,	ptcclose,	ptcread,	ptcwrite,	/*21*/
***************
*** 514,516
  	ptyioctl,	nulldev,	nodev,		pt_tty,
! 	ptcselect,	nodev,
  	dmfopen,	dmfclose,	dmfread,	dmfwrite,	/*22*/

--- 705,708 -----
  	ptyioctl,	nulldev,	nodev,		pt_tty,
! 	ptcselect,	nodev,		0,		0,
! 
  	dmfopen,	dmfclose,	dmfread,	dmfwrite,	/*22*/
***************
*** 517,519
  	dmfioctl,	dmfstop,	dmfreset,	dmf_tty,
! 	ttselect,	nodev,
  	idcopen,	nulldev,	idcread,	idcwrite,	/*23*/

--- 709,712 -----
  	dmfioctl,	dmfstop,	dmfreset,	dmf_tty,
! 	ttselect,	nodev,		0,		0,
! 
  	idcopen,	nulldev,	idcread,	idcwrite,	/*23*/
***************
*** 520,522
  	nodev,		nodev,		idcreset,	0,
! 	seltrue,	nodev,
  	dnopen,		dnclose,	nodev,		dnwrite,	/*24*/

--- 713,716 -----
  	nodev,		nodev,		idcreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	dnopen,		dnclose,	nodev,		dnwrite,	/*24*/
***************
*** 523,526
  	nodev,		nodev,		nodev,		0,
! 	seltrue,	nodev,
! /* 25-29 reserved to local sites */
  	gpibopen,	gpibclose,	gpibread,	gpibwrite,	/*25*/

--- 717,720 -----
  	nodev,		nodev,		nodev,		0,
! 	seltrue,	nodev,		0,		0,
! 
  	gpibopen,	gpibclose,	gpibread,	gpibwrite,	/*25*/
***************
*** 527,529
  	gpibioctl,	nulldev,	nodev,		0,
! 	seltrue,	nodev,
  	lpaopen,	lpaclose,	lparead,	lpawrite,	/*26*/

--- 721,724 -----
  	gpibioctl,	nulldev,	nodev,		0,
! 	seltrue,	nodev,		0,		0,
! 
  	lpaopen,	lpaclose,	lparead,	lpawrite,	/*26*/
***************
*** 530,532
  	lpaioctl,	nodev,		nulldev,	0,
! 	seltrue,	nodev,
  	psopen,		psclose,	psread,		pswrite,	/*27*/

--- 725,728 -----
  	lpaioctl,	nodev,		nulldev,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	psopen,		psclose,	psread,		pswrite,	/*27*/
***************
*** 533,535
  	psioctl,	nodev,		psreset,	0,
! 	seltrue,	nodev,
  	ibopen,		ibclose,	ibread,		ibwrite,	/*28*/

--- 729,732 -----
  	psioctl,	nodev,		psreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	ibopen,		ibclose,	ibread,		ibwrite,	/*28*/
***************
*** 536,538
  	ibioctl,	nodev,		nodev,		0,
! 	seltrue,	nodev,
  	adopen,		adclose,	nodev,		nodev,		/*29*/

--- 733,736 -----
  	ibioctl,	nodev,		nodev,		0,
! 	seltrue,	nodev,		0,		0,
! 
  	adopen,		adclose,	nodev,		nodev,		/*29*/
***************
*** 539,541
  	adioctl,	nodev,		adreset,	0,
! 	seltrue,	nodev,
  	rxopen,		rxclose,	rxread,		rxwrite,	/*30*/

--- 737,740 -----
  	adioctl,	nodev,		adreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	rxopen,		rxclose,	rxread,		rxwrite,	/*30*/
***************
*** 542,544
  	rxioctl,	nodev,		rxreset,	0,
! 	seltrue,	nodev,
  	ikopen,		ikclose,	ikread,		ikwrite,	/*31*/

--- 741,744 -----
  	rxioctl,	nodev,		rxreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
  	ikopen,		ikclose,	ikread,		ikwrite,	/*31*/
***************
*** 545,548
  	ikioctl,	nodev,		ikreset,	0,
! 	seltrue,	nodev,
! 	rlopen,		nodev,		rlread,		rlwrite,	/* 32 */
  	nodev,		nodev,		rlreset,	0,

--- 745,749 -----
  	ikioctl,	nodev,		ikreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
! 	rlopen,		nodev,		rlread,		rlwrite,	/*32*/
  	nodev,		nodev,		rlreset,	0,
*************** Here come lots of additions.  The mass driver entry
*************** is at the end of these.
*** 548,550
  	nodev,		nodev,		rlreset,	0,
! 	seltrue,	nodev,
  };

--- 749,813 -----
  	nodev,		nodev,		rlreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
! /* BRL additions */
! 
! 	rfopen,		nulldev,	rfread,		rfwrite,	/*33*/
! 	nodev,		nodev,		rfreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
! 	vgopen,		vgclose,	vgread,		vgwrite,	/*34*/
! 	vgioctl,	nodev,		vgreset,	0,
! 	vgselect,	nodev,		0,		0,
! 
! 	umpopen,	umpclose,	umpread,	umpwrite,	/*35*/
! 	umpioctl,	nodev,		nulldev,	0,
! 	umpselect,	nodev,		0,		0,
! 
! 	hswopen,	hswclose,	hswread,	hswwrite,	/*36*/
! 	nodev,		nodev,		nulldev,	0,
! 	hswselect,	nodev,		0,		0,
! 
! 	lsbopen,	lsbclose,	lsbread,	lsbwrite,	/*37*/
! 	nodev,		nodev,		lsbreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
! 	mcdopen,	mcdclose,	mcdread,	mcdwrite,	/*38*/
! 	nodev,		nodev,		mcdreset,	0,
! 	seltrue,	nodev,		0,		0,
! 
! 	mssopen,	mssclose,	nodev,		nodev,		/*39*/
! 	mssioctl,	nodev,		mssreset,	0,
! 	mssselect,	nodev,		0,		0,
! 
! 	mgopen,		mgclose,	mgread,		mgwrite,	/*40*/
! 	mgioctl,	nodev,		mgreset,	0,
! 	mgselect,	nodev,		0,		0,
! 
! /* U of MD additions */
! 
! 	dzoopen,	dzoclose,	dzread,		dzwrite,	/*41*/
! 	dzioctl,	dzstop,		nulldev,	dz_tty,
! 	ttselect,	nodev,		0,		0,
! 
! 	dhoopen,	dhoclose,	dhread,		dhwrite,	/*42*/
! 	dhioctl,	dhstop,		nulldev,	dh11,
! 	ttselect,	nodev,		0,		0,
! 
! 	dhpopen,	dhpclose,	dhread,		dhwrite,	/*43*/
! 	dhioctl,	dhstop,		nulldev,	dh11,
! 	ttselect,	nodev,		0,		0,
! 
! 	fdopen,		nodev,		nodev,		nodev,		/*44*/
! 	nodev,		nodev,		nodev,		0,
! 	nodev,		nodev,		0,		0,
! 
! /* moblet multiplexor device: FLB */
! 	zmopen,		zmclose,	zmread,		zmwrite,	/*45*/
! 	zmioctl,	nodev,		nulldev,	0,
! 	seltrue,	nodev,		0,		0,
! 
! /* mass driver */
! 	drivopen,	drivclose,	drivread,	drivwrite,	/*46*/
! 	drivioctl,	nodev,		nulldev,	0,
! 	drivselect,	nodev,		0,		0,
  };
*************** Just a minor format gripe.
*** 550,551
  };
  int	nchrdev = sizeof (cdevsw) / sizeof (cdevsw[0]);

--- 813,815 -----
  };
+ 
  int	nchrdev = sizeof (cdevsw) / sizeof (cdevsw[0]);
RCS file: RCS/conf.h,v
retrieving revision 1.1
diff -b -c1 -r1.1 conf.h
*** /tmp/,RCSt1003100	Fri Jan  4 22:59:28 1985
--- conf.h	Thu Jan  3 11:58:43 1985
*************** Here we have the new entries in character (raw) devices
*************** to allow them to be used by the mass driver.
*** 39,40
  	int	(*d_mmap)();
  };

--- 39,42 -----
  	int	(*d_mmap)();
+ 	int	(*d_mass)();
+ 	int	(*d_bstrat)();
  };
RCS file: RCS/fs.h,v
retrieving revision 1.1
diff -b -c1 -r1.1 fs.h
*** /tmp/,RCSt1003110	Fri Jan  4 22:59:37 1985
--- fs.h	Wed Jan  2 09:50:21 1985
*************** Minor glitch in comments fixed.
*** 89,92
   * within the bounds dictated by MINBSIZE.
!  * Note that super blocks are always of size MAXBSIZE,
!  * and that MAXBSIZE must be >= MINBSIZE.
   */

--- 89,92 -----
   * within the bounds dictated by MINBSIZE.
!  * Note that super blocks are always of size SBSIZE,
!  * and that both SBSIZE and MAXBSIZE must be >= MINBSIZE.
   */
RCS file: RCS/ht.c,v
retrieving revision 1.1
diff -b -c1 -r1.1 ht.c
*** /tmp/,RCSt1003196	Fri Jan  4 23:01:37 1985
--- ht.c	Wed Jan  2 12:19:05 1985
*************** Don't need conf.h in ht.c.
*** 18,20
  #include "../h/buf.h"
- #include "../h/conf.h"
  #include "../h/dir.h"

--- 18,19 -----
  #include "../h/buf.h"
  #include "../h/dir.h"
*************** The BRL "commented sleep" which you don't have and thus
*************** can't use...
*** 173,175
  		bp->b_flags |= B_WANTED;
! 		sleep((caddr_t)bp, PRIBIO);
  	}

--- 172,174 -----
  		bp->b_flags |= B_WANTED;
! 		csleep((caddr_t)bp, PRIBIO, "TAPE");
  	}
*************** Here comes the real mass driver change (actually, addition):
*** 454,455
  	return (0);
  }

--- 453,469 -----
  	return (0);
+ }
+ 
+ /*
+  * Start a ``raw block transfer'' from kernel space (via the
+  * mass driver).
+  */
+ htmass(bp)
+ 	register struct buf *bp;
+ {
+ 	register struct tu_softc *sc;
+ 
+ 	minphys(bp);
+ 	sc = &tu_softc[TUUNIT(bp->b_dev)];
+ 	sc->sc_blkno = bdbtofsb(bp->b_blkno);
+ 	sc->sc_nxrec = sc->sc_blkno + 1;
  }
RCS file: RCS/machdep.c,v
retrieving revision 1.1
diff -b -c1 -r1.1 machdep.c
*** /tmp/,RCSt1003151	Fri Jan  4 23:00:37 1985
--- machdep.c	Wed Jan  2 10:38:44 1985
*************** Don't ask me!
*** 1,2
! /*	machdep.c	6.2	83/10/02	*/
  

--- 1,2 -----
! /*	machdep.c	6.4 (modified)	84/02/02	*/
  
*************** Rearranging a few parameters.  You want these changes....
*** 66,68
  	register struct pte *pte;
- 	int mapaddr, j;
  	register caddr_t v;

--- 66,67 -----
  	register struct pte *pte;
  	register caddr_t v;
***************
*** 68,70
  	register caddr_t v;
! 	int maxbufs, base, residual;
  	extern char etext;

--- 67,70 -----
  	register caddr_t v;
! 	register int mapaddr, j;
! 	int t, base, residual, spaceleft;
  	extern char etext;
*************** This next little bit is for a title which we put in the
*************** version file.  Since you don't do this, you don't want
*************** this particular change.  (I'll tell you when to start
*************** looking again.)
*** 70,71
  	extern char etext;
  

--- 70,72 -----
  	extern char etext;
+ 	extern char vers_title[];
  
***************
*** 83,84
  	 */
  	printf(version);

--- 84,92 -----
  	 */
+ 	if (vers_title[0]) {
+ 		i = strlen(version) - 1;	/* sigh */
+ 		version[i] = 0;
+ 		printf("%s (%s)\n", version, vers_title);
+ 		version[i] = '\n';
+ 	}
+ 	else
  		printf(version);
*************** Start looking again.  Here we change buffer allocation
*************** policies...
*** 90,91
  	 * We allocate 1/2 as many swap buffer headers as file i/o buffers.
  	 */

--- 98,101 -----
  	 * We allocate 1/2 as many swap buffer headers as file i/o buffers.
+ 	 * MAXBSIZE is probably way bigger than most of the buffers
+ 	 * we'll need, so we assume buffers of "average" size AVGBSIZE.
  	 */
***************
*** 91,94
  	 */
- 	maxbufs = ((SYSPTSIZE * NBPG) - (5 * (int)(&etext - 0x80000000))) /
- 	    MAXBSIZE;
  	if (bufpages == 0)

--- 101,102 -----
  	 */
  	if (bufpages == 0)
***************
*** 96,98
  	if (nbuf == 0) {
! 		nbuf = bufpages / 2;
  		if (nbuf < 16)

--- 104,118 -----
  	if (nbuf == 0) {
! 		nbuf = bufpages * CLBYTES / AVGBSIZE;
! 		/*
! 		 * We need enough space to map everything, so we assume
! 		 * about 700K has to be left after mapping the buffers.
! 		 * (It takes only 6K of pte's to map this, so if we need
! 		 * less, we haven't wasted much memory.)
! 		 */
! 		t = ((SYSPTSIZE * NBPG) - 786432) / MAXBSIZE;
! 		if (nbuf > t) {
! 			printf("sys pt too small; want %d buffers", nbuf);
! 			nbuf = t;
! 			printf(" but space for only %d\n", nbuf);
! 		}
  		if (nbuf < 16)
***************
*** 99,102
  			nbuf = 16;
- 		if (nbuf > maxbufs)
- 			nbuf = maxbufs;
  	}

--- 119,120 -----
  			nbuf = 16;
  	}
***************
*** 102,105
  	}
! 	if (bufpages > nbuf * (MAXBSIZE / CLBYTES))
! 		bufpages = nbuf * (MAXBSIZE / CLBYTES);
  	if (nswbuf == 0) {

--- 120,123 -----
  	}
! 	if (bufpages > (nbuf * AVGBSIZE) / CLBYTES)
! 		bufpages = (nbuf * AVGBSIZE) / CLBYTES;
  	if (nswbuf == 0) {
***************
*** 125,126
  	    (name) = (type *)(v); (v) = (caddr_t)((lim) = ((name)+(num)))
  	valloc(buffers, char, MAXBSIZE * nbuf);

--- 143,152 -----
  	    (name) = (type *)(v); (v) = (caddr_t)((lim) = ((name)+(num)))
+ 	/*
+ 	 * Map the buffers first, because they are only partially mapped
+ 	 * (that is to say, they get less physical memory than virtual).
+ 	 * Each buffer maps a full MAXBSIZE bytes but (at least initially)
+ 	 * contains only 1/nbuf'th of the total buffer pool, with the
+ 	 * "extra" pages (if any) given to the first bufpages%nbuf buffers.
+ 	 * binit() (in ../sys/init_main.c) uses this same convention.
+ 	 */
  	valloc(buffers, char, MAXBSIZE * nbuf);
***************
*** 128,131
  	residual = bufpages % nbuf;
! 	for (i = 0; i < residual; i++) {
! 		for (j = 0; j < (base + 1) * CLSIZE; j++) {
  			*(int *)(&Sysmap[mapaddr+j]) = PG_V | PG_KW | firstaddr;

--- 154,162 -----
  	residual = bufpages % nbuf;
! 	for (i = 0; i < nbuf; i++) {
! 		if (i < residual)
! 			t = base + 1;
! 		else
! 			t = base;
! 		t *= CLSIZE;
! 		for (j = 0; j < t; j++) {
  			*(int *)(&Sysmap[mapaddr+j]) = PG_V | PG_KW | firstaddr;
***************
*** 136,145
  	}
! 	for (i = residual; i < nbuf; i++) {
! 		for (j = 0; j < base * CLSIZE; j++) {
! 			*(int *)(&Sysmap[mapaddr+j]) = PG_V | PG_KW | firstaddr;
! 			clearseg((unsigned)firstaddr);
! 			firstaddr++;
! 		}
! 		mapaddr += MAXBSIZE / NBPG;
! 	}
  	valloc(buf, struct buf, nbuf);

--- 167,171 -----
  	}
! 	/*
! 	 * Everything from here on is fully mapped.
! 	 */
  	valloc(buf, struct buf, nbuf);
***************
*** 161,163
  	/*
! 	 * Now allocate space for core map
  	 * Allow space for all of phsical memory minus the amount 

--- 187,189 -----
  	/*
! 	 * Now allocate space for core map.
  	 * Allow space for all of phsical memory minus the amount 
***************
*** 163,168
  	 * Allow space for all of phsical memory minus the amount 
! 	 * dedicated to the system. The amount of physical memory
! 	 * dedicated to the system is the total virtual memory of
! 	 * the system minus the space in the buffers which is not
! 	 * allocated real memory.
  	 */

--- 189,207 -----
  	 * Allow space for all of phsical memory minus the amount 
! 	 * dedicated to the system ("kernel memory").  The amount
! 	 * of kernel memory is equal to v-0x80000000 minus the
! 	 * holes in the buffers.  In other words:
! 	 *
! 	 * kernel mem =     mem used	-	mem not REALLY used
! 	 *	      = (v&~0x80000000) - (nbuf*MAXBSIZE-bufpages*CLBYTES)
! 	 * space left = total space  - kernel space
! 	 *	      = physmem*NBPG - kernel space
! 	 *
! 	 * (Of course, when we allocate the core map, we use up some of
! 	 * that left-over space, thus the formula for ncmap below.)
! 	 * There is, however, no guarantee that we have enough pte's
! 	 * to map everything.  Here we calculate the ideal size for the
! 	 * core map, find out how much space is left in the system page
! 	 * table, and allocate the minimum of the two numbers.  (It
! 	 * currently takes about 100K to map 6M [with CLSIZE == 2 and 
! 	 * sizeof (struct cmap) == 16]).
  	 */
***************
*** 168,174
  	 */
! 	ncmap = (physmem*NBPG - ((int)v &~ 0x80000000) +
! 		(nbuf * (MAXBSIZE - 2 * CLBYTES))) /
! 		    (NBPG*CLSIZE + sizeof (struct cmap));
! 	valloclim(cmap, struct cmap, ncmap, ecmap);
! 	if ((((int)(ecmap+1))&~0x80000000) > SYSPTSIZE*NBPG)
  		panic("sys pt too small");

--- 207,218 -----
  	 */
! 	spaceleft = physmem*NBPG - ((int)v & ~0x80000000) +
! 	    nbuf*MAXBSIZE - bufpages*CLBYTES;
! 	if (spaceleft < 0) {
! 		printf("Kernel wants %d more bytes than exist!\n",
! 			-spaceleft);
! 		panic("kernel size");
! 	}
! 	ncmap = spaceleft / (CLBYTES + sizeof (struct cmap));
! 	spaceleft = SYSPTSIZE*NBPG - (((int)(v + 1)) & ~0x80000000);
! 	if (spaceleft < 0)
  		panic("sys pt too small");
***************
*** 174,176
  		panic("sys pt too small");
! 
  	/*

--- 218,228 -----
  		panic("sys pt too small");
! 	if (ncmap * sizeof (struct cmap) > spaceleft) {
! 		printf("sys pt too small; trimmed core map\n");
! 		ncmap = spaceleft / sizeof (struct cmap);
! 	}
! 	valloclim(cmap, struct cmap, ncmap, ecmap);
! 	/* anti-bug test: */
! 	spaceleft = SYSPTSIZE*NBPG - (((int)(ecmap+1))&~0x80000000);
! 	if (spaceleft < 0)
! 		panic("startup");
  	/*
*************** (The part about #if GYRE is for our kernel hack machine.)
*** 176,177
  	/*
  	 * Clear allocated space, and make r/w entries

--- 228,242 -----
  	/*
+ 	 * If there's "lots" of wasted space, gripe.
+ 	 * (Lots == at least 5% of physical memory in wasted page tables.
+ 	 * Spaceleft is currently the amount of memory we can still map.)
+ 	 */
+ 	spaceleft /= NBPG * NPTEPG;	/* convert to true pages */
+ #if GYRE
+ 	if (spaceleft >= NBPG)
+ 		printf("%d extra pages in sys pt\n", spaceleft / NBPG);
+ #else
+ 	if (spaceleft >= physmem / 20)
+ 		printf("%d extra pages in sys pt\n", spaceleft / NBPG);
+ #endif
+ 	/*
  	 * Clear allocated space, and make r/w entries
*************** This is part of one of RWS@MIT-BOLD's fixes.  I don't know
*************** if it would hurt to put it in if you haven't already... it's
*************** up to you.
*** 286,288
  	 */
! 	if (!oonstack && (int)fp <= USRSTACK - ctob(u.u_ssize)) 
  		grow((unsigned)fp);

--- 351,353 -----
  	 */
! 	if ((int)fp <= USRSTACK - ctob(u.u_ssize)) 
  		grow((unsigned)fp);
*************** I thought this deserved a comment.
*** 288,290
  		grow((unsigned)fp);
! 	;
  #ifndef lint

--- 353,355 -----
  		grow((unsigned)fp);
! 	;			/* Avoid asm() label botch */
  #ifndef lint
*************** More of those fixes from RWS@MIT-BOLD.
*** 296,298
  #endif
! 	if (!u.u_onstack && (int)scp <= USRSTACK - ctob(u.u_ssize))
  		grow((unsigned)scp);

--- 361,363 -----
  #endif
! 	if ((int)scp <= USRSTACK - ctob(u.u_ssize))
  		grow((unsigned)scp);
*************** (Need I say anything?)
*** 360,361
  		return;
  #ifndef lint

--- 425,427 -----
  		return;
+ 	;			/* Avoid asm() label botch */
  #ifndef lint
*************** Part of the changes to handle 64K chip controllers.  This
*************** is not everything you need, so don't install it.
*** 426,428
  		mcr = mcraddr[m];
! 		switch (cpu) {
  #if VAX780

--- 492,494 -----
  		mcr = mcraddr[m];
! 		switch (mcrtype[m]) {
  #if VAX780
***************
*** 428,431
  #if VAX780
! 		case VAX_780:
! 			M780_ENA(mcr);
  			break;

--- 494,497 -----
  #if VAX780
! 		case M780C:
! 			M780C_ENA(mcr);
  			break;
***************
*** 431,432
  			break;
  #endif

--- 497,504 -----
  			break;
+ 		case M780EL:
+ 			M780EL_ENA(mcr);
+ 			break;
+ 		case M780EU:
+ 			M780EU_ENA(mcr);
+ 			break;
  #endif
***************
*** 433,435
  #if VAX750
! 		case VAX_750:
  			M750_ENA(mcr);

--- 505,507 -----
  #if VAX750
! 		case M750:
  			M750_ENA(mcr);
***************
*** 438,440
  #if VAX730
! 		case VAX_730:
  			M730_ENA(mcr);

--- 510,512 -----
  #if VAX730
! 		case M730:
  			M730_ENA(mcr);
***************
*** 461,463
  		mcr = mcraddr[m];
! 		switch (cpu) {
  #if VAX780

--- 533,535 -----
  		mcr = mcraddr[m];
! 		switch (mcrtype[m]) {
  #if VAX780
***************
*** 463,466
  #if VAX780
! 		case VAX_780:
! 			if (M780_ERR(mcr)) {
  				printf("mcr%d: soft ecc addr %x syn %x\n",

--- 535,538 -----
  #if VAX780
! 		case M780C:
! 			if (M780C_ERR(mcr)) {
  				printf("mcr%d: soft ecc addr %x syn %x\n",
***************
*** 466,468
  				printf("mcr%d: soft ecc addr %x syn %x\n",
! 				    m, M780_ADDR(mcr), M780_SYN(mcr));
  #ifdef TRENDATA

--- 538,540 -----
  				printf("mcr%d: soft ecc addr %x syn %x\n",
! 				    m, M780C_ADDR(mcr), M780C_SYN(mcr));
  #ifdef TRENDATA
***************
*** 470,472
  #endif
! 				M780_INH(mcr);
  			}

--- 542,544 -----
  #endif
! 				M780C_INH(mcr);
  			}
***************
*** 473,474
  			break;
  #endif

--- 545,562 -----
  			break;
+ 
+ 		case M780EL:
+ 			if (M780EL_ERR(mcr)) {
+ 				printf("mcr%d: soft ecc addr %x syn %x\n",
+ 				    m, M780EL_ADDR(mcr), M780EL_SYN(mcr));
+ 				M780EL_INH(mcr);
+ 			}
+ 			break;
+ 
+ 		case M780EU:
+ 			if (M780EU_ERR(mcr)) {
+ 				printf("mcr%d: soft ecc addr %x syn %x\n",
+ 				    m, M780EU_ADDR(mcr), M780EU_SYN(mcr));
+ 				M780EU_INH(mcr);
+ 			}
+ 			break;
  #endif
***************
*** 475,477
  #if VAX750
! 		case VAX_750:
  			if (M750_ERR(mcr)) {

--- 563,565 -----
  #if VAX750
! 		case M750:
  			if (M750_ERR(mcr)) {
***************
*** 486,488
  #if VAX730
! 		case VAX_730: {
  			register int mcreg = mcr->mc_reg[1];

--- 574,576 -----
  #if VAX730
! 		case M730: {
  			register int mcreg = mcr->mc_reg[1];
***************
*** 539,541
  
! 	switch (cpu) {
  

--- 627,629 -----
  
! 	switch (mcrtype[m]) {
  
***************
*** 542,544
  #if VAX780
! 	case VAX_780:
  	for (i = 0; i < (sizeof (memlogtab) / sizeof (memlogtab[0])); i++)

--- 630,632 -----
  #if VAX780
! 	case M780C:
  	for (i = 0; i < (sizeof (memlogtab) / sizeof (memlogtab[0])); i++)
***************
*** 544,546
  	for (i = 0; i < (sizeof (memlogtab) / sizeof (memlogtab[0])); i++)
! 		if ((u_char)(M780_SYN(mcr)) == memlogtab[i].m_syndrome) {
  			printf (

--- 632,634 -----
  	for (i = 0; i < (sizeof (memlogtab) / sizeof (memlogtab[0])); i++)
! 		if ((u_char)(M780C_SYN(mcr)) == memlogtab[i].m_syndrome) {
  			printf (
***************
*** 549,552
  				memlogtab[i].m_chip,
! 				(M780_ADDR(mcr) & 0x8000) ? "upper" : "lower",
! 				(M780_ADDR(mcr) >> 16));
  			return;

--- 637,640 -----
  				memlogtab[i].m_chip,
! 				(M780C_ADDR(mcr) & 0x8000) ? "upper" : "lower",
! 				(M780C_ADDR(mcr) >> 16));
  			return;
*************** Here come some irrelevant but perhaps useful changes
*************** in the panic/reboot code.
*** 583,584
  
  boot(paniced, arghowto)

--- 671,675 -----
  
+ /*
+  * Reboot.
+  */
  boot(paniced, arghowto)
***************
*** 588,589
  	register int devtype;		/* r10 == major of root dev */
  

--- 679,682 -----
  	register int devtype;		/* r10 == major of root dev */
+ 	register struct buf *bp;
+ 	register iter, nbusy;
  
***************
*** 593,595
  #endif
! 	(void) spl1();
  	howto = arghowto;

--- 686,692 -----
  #endif
! 	/*
! 	 * Figure out whether we can and should complete sandbagged I/O;
! 	 * if so, wait for no busy buffers or a maximum time of ~1 sec.
! 	 */
! 	mtpr(IPL, 0x1f);		/* block EVERYTHING */
  	howto = arghowto;
***************
*** 597,598
  		waittime = 0;
  		update();

--- 694,696 -----
  		waittime = 0;
+ 		(void) spl1();
  		update();
***************
*** 599,607
  		printf("syncing disks... ");
! #ifdef notdef
! 		DELAY(10000000);
! #else
! 		{ register struct buf *bp;
! 		  int iter, nbusy;
! 
! 		  for (iter = 0; iter < 20; iter++) {
  			nbusy = 0;

--- 697,699 -----
  		printf("syncing disks... ");
! 		for (iter = 0; iter < 32; iter++) {
  			nbusy = 0;
***************
*** 613,614
  			printf("%d ", nbusy);
  		  }

--- 705,707 -----
  			printf("%d ", nbusy);
+ 			DELAY(20000);
  		}
***************
*** 614,617
  		  }
! 		}
! #endif
  		printf("done\n");

--- 707,710 -----
  		}
! 		if (nbusy != 0)
! 			printf("%d bufs un", nbusy);
  		printf("done\n");
***************
*** 617,618
  		printf("done\n");
  	}

--- 710,712 -----
  		printf("done\n");
+ 		mtpr(IPL, 0x1f);
  	}
***************
*** 618,620
  	}
- 	splx(0x1f);			/* extreme priority */
  	devtype = major(rootdev);

--- 712,713 -----
  	}
  	devtype = major(rootdev);
***************
*** 625,627
  			;
! 	} else {
  		if (paniced == RB_PANIC) {

--- 718,721 -----
  			;
! 	}
! 	else {
  		if (paniced == RB_PANIC) {
***************
*** 627,629
  		if (paniced == RB_PANIC) {
! 			doadump();		/* TXDB_BOOT's itsself */
  			/*NOTREACHED*/

--- 721,723 -----
  		if (paniced == RB_PANIC) {
! 			doadump();	/* TXDB_BOOT's itself */
  			/*NOTREACHED*/
***************
*** 633,636
  #if defined(VAX750) || defined(VAX730)
! 	if (cpu != VAX_780)
! 		{ asm("movl r11,r5"); }		/* boot flags go in r5 */
  #endif

--- 727,733 -----
  #if defined(VAX750) || defined(VAX730)
! 	/*
! 	 * 750s and 730s want their boot flags in r5 (rather than r11).
! 	 * Doesn't hurt 780s, so just do it all the time.
! 	 */
! 	asm("movl r11,r5");
  #endif
***************
*** 636,638
  #endif
! 	for (;;)
  		asm("halt");

--- 733,735 -----
  #endif
! 	for (;;) {
  		asm("halt");
***************
*** 638,639
  		asm("halt");
  	/*NOTREACHED*/

--- 735,737 -----
  		asm("halt");
+ 	}
  	/*NOTREACHED*/
*************** (Part of?) wls@astrovax's fix for tbuf parity errors.
*************** If you haven't installed this, do so, maybe it will
*************** help a bit.
*** 809,811
  		mtpr(MCESR, 0xf);
! 		if ((mcf->mc5_mcesr&0xf) == MC750_TBPAR) {
  			printf("tbuf par: flushing and returning\n");

--- 907,909 -----
  		mtpr(MCESR, 0xf);
! 		if ((mcf->mc5_mcesr&0xe) == MC750_TBPAR) {
  			printf("tbuf par: flushing and returning\n");
*************** More BRL csleep changes that you should ignore
*** 860,862
  	while ((bp->b_flags & B_DONE) == 0)
! 		sleep((caddr_t)bp, prio);
  	splx(s);

--- 958,960 -----
  	while ((bp->b_flags & B_DONE) == 0)
! 		csleep((caddr_t)bp, prio, "PHYSIO");
  	splx(s);
***************
*** 863
  }

--- 961,962 -----
  }
+ 
RCS file: RCS/mba.c,v
retrieving revision 1.1
diff -b -c1 -r1.1 mba.c
*** /tmp/,RCSt1003207	Fri Jan  4 23:01:50 1985
--- mba.c	Fri Jan  4 19:34:40 1985
*************** Another useless "conf.h"!
*** 13,15
  #include "../h/buf.h"
- #include "../h/conf.h"
  #include "../h/dir.h"

--- 13,14 -----
  #include "../h/buf.h"
  #include "../h/dir.h"
*************** Track the b_active flag better.  (Part of the mass driver
*************** changes.)  Just about everything else in this file is more
*************** of the same.
*** 46,48
  	bp = mi->mi_tab.b_actf;
! 	if (bp == NULL)
  		return;

--- 45,48 -----
  	bp = mi->mi_tab.b_actf;
! 	if (bp == NULL) {
! 		mi->mi_tab.b_active = 0;
  		return;
***************
*** 48,49
  		return;
  	/*

--- 48,51 -----
  		return;
+ 	}
+ 	mi->mi_tab.b_active = 1;
  	/*
***************
*** 56,58
  		mi->mi_tab.b_actf = bp->av_forw;
- 		mi->mi_tab.b_active = 0;
  		mi->mi_tab.b_errcnt = 0;

--- 58,59 -----
  		mi->mi_tab.b_actf = bp->av_forw;
  		mi->mi_tab.b_errcnt = 0;
***************
*** 69,71
  	case MBU_NEXT:		/* request is complete (e.g. ``sense'') */
- 		mi->mi_tab.b_active = 0;
  		mi->mi_tab.b_errcnt = 0;

--- 70,71 -----
  	case MBU_NEXT:		/* request is complete (e.g. ``sense'') */
  		mi->mi_tab.b_errcnt = 0;
***************
*** 91,93
  		 * In any case the device is ``active'' waiting for the
! 		 * data to transfer.
  		 */

--- 91,93 -----
  		 * In any case the device is ``active'' waiting for the
! 		 * data to transfer.  (mi->mi_tab.b_active was set earlier.)
  		 */
***************
*** 93,95
  		 */
- 		mi->mi_tab.b_active = 1;
  		if (mhp->mh_active == 0)

--- 93,94 -----
  		 */
  		if (mhp->mh_active == 0)
***************
*** 107,109
  		}
- 		mi->mi_tab.b_active = 1;
  		return;

--- 106,107 -----
  		}
  		return;
***************
*** 116,117
  		mi->mi_tab.b_flags |= B_BUSY;
  		return;

--- 114,116 -----
  		mi->mi_tab.b_flags |= B_BUSY;
+ 		mi->mi_tab.b_active = 0;	/* ??? */
  		return;
***************
*** 143,144
  	if ((mi = mhp->mh_actf) == NULL) {
  		return;

--- 142,144 -----
  	if ((mi = mhp->mh_actf) == NULL) {
+ 		mhp->mh_active = 0;
  		return;
***************
*** 146,147
  	if ((bp = mi->mi_tab.b_actf) == NULL) {
  		mhp->mh_actf = mi->mi_forw;

--- 146,148 -----
  	if ((bp = mi->mi_tab.b_actf) == NULL) {
+ 		mi->mi_tab.b_active = 0;
  		mhp->mh_actf = mi->mi_forw;
***************
*** 149,150
  	}
  	/*

--- 150,153 -----
  	}
+ 
+ 	mhp->mh_active = 1;
  	/*
***************
*** 167,169
  		mi->mi_tab.b_errcnt = 0;
- 		mi->mi_tab.b_active = 0;
  		bp->b_flags |= B_ERROR;

--- 170,171 -----
  		mi->mi_tab.b_errcnt = 0;
  		bp->b_flags |= B_ERROR;
***************
*** 173,178
  	/*
! 	 * We can do the operation; mark the massbus active
! 	 * and let the device start routine setup any necessary
! 	 * device state for the transfer (e.g. desired cylinder, etc
! 	 * on disks).
  	 */

--- 175,179 -----
  	/*
! 	 * We can do the operation; let the device start routine
! 	 * set up any necessary device state for the transfer
! 	 * (e.g. desired cylinder, etc on disks).
  	 */
***************
*** 178,180
  	 */
- 	mhp->mh_active = 1;
  	if (mi->mi_driver->md_start) {

--- 179,180 -----
  	 */
  	if (mi->mi_driver->md_start) {
*************** Part of this is a fix from thomas@utah-gr which you should
*************** install anyway, whether or not you are installing the mass
*************** driver.  (In other words, keep going.)
*** 238,239
  	 * are now finished.
  	 */

--- 238,242 -----
  	 * are now finished.
+ 	 * Data transfer complete interrupts do not set the attn
+ 	 * bit, and positioning commands (e.g. recal) do not set
+ 	 * DTCMP.
  	 */
***************
*** 239,241
  	 */
! 	if (mhp->mh_active) {
  		/*

--- 242,245 -----
  	 */
! 	mi = mhp->mh_actf;
! 	if (mhp->mh_active && (mbasr&MBSR_DTCMP || as&(1<<mi->mi_drive))) {
  		/*
***************
*** 246,248
  		 */
- 		mi = mhp->mh_actf;
  		as &= ~(1 << mi->mi_drive);

--- 250,251 -----
  		 */
  		as &= ~(1 << mi->mi_drive);
***************
*** 324,326
  			case MBN_DONE:		/* operation completed */
- 				mi->mi_tab.b_active = 0;
  				mi->mi_tab.b_errcnt = 0;

--- 327,328 -----
  			case MBN_DONE:		/* operation completed */
  				mi->mi_tab.b_errcnt = 0;
***************
*** 329,330
  				iodone(bp);
  				/* fall into common code */

--- 331,333 -----
  				iodone(bp);
+ 				mi->mi_tab.b_active = 0;
  				/* fall into common code */
*************** Here's some stuff I did 'cause I thought the code could
*************** be cleaned up some.  Well, it *is* *slightly* shorter....
*************** (This isn't necessary but can't hurt [famous last words]).
*** 383,385
  /*
!  * Setup the mapping registers for a transfer.
   */

--- 386,389 -----
  /*
!  * Set up the mapping registers for a transfer.
!  * Leave a zero entry at the end to guard against wild transfers.
   */
***************
*** 388,396
  {
! 	register struct mba_regs *mbap = mi->mi_mba;
! 	struct buf *bp = mi->mi_tab.b_actf;
! 	register int npf;
! 	unsigned v;
! 	register struct pte *pte, *io;
! 	int o;
! 	struct proc *rp;
  

--- 392,396 -----
  {
! 	register struct buf *bp = mi->mi_tab.b_actf;
! 	register struct pte *pte;
! 	register int o;
  
***************
*** 396,398
  
- 	v = btop(bp->b_un.b_addr);
  	o = (int)bp->b_un.b_addr & PGOFSET;

--- 396,397 -----
  
  	o = (int)bp->b_un.b_addr & PGOFSET;
***************
*** 398,401
  	o = (int)bp->b_un.b_addr & PGOFSET;
- 	npf = btoc(bp->b_bcount + o);
- 	rp = bp->b_flags&B_DIRTY ? &proc[2] : bp->b_proc;
  	if ((bp->b_flags & B_PHYS) == 0)

--- 397,398 -----
  	o = (int)bp->b_un.b_addr & PGOFSET;
  	if ((bp->b_flags & B_PHYS) == 0)
***************
*** 401,404
  	if ((bp->b_flags & B_PHYS) == 0)
! 		pte = &Sysmap[btop(((int)bp->b_un.b_addr)&0x7fffffff)];
! 	else if (bp->b_flags & B_UAREA)
  		pte = &rp->p_addr[v];

--- 398,406 -----
  	if ((bp->b_flags & B_PHYS) == 0)
! 		pte = &Sysmap[btop((int)bp->b_un.b_addr & 0x7fffffff)];
! 	else {
! 		register struct proc *rp;
! 		register unsigned v = btop(bp->b_un.b_addr);
! 		
! 		rp = bp->b_flags&B_DIRTY ? &proc[2] : bp->b_proc;
! 		if (bp->b_flags & B_UAREA)
  			pte = &rp->p_addr[v];
***************
*** 408,410
  		pte = vtopte(rp, v);
! 	io = mbap->mba_map;
  	while (--npf >= 0) {

--- 410,416 -----
  			pte = vtopte(rp, v);
! 	}
! 	{
! 		register struct pte *io = mi->mi_mba->mba_map;
! 		register int npf = btoc(bp->b_bcount + o);
! 
  		while (--npf >= 0) {
***************
*** 411,413
  		if (pte->pg_pfnum == 0)
! 			panic("mba, zero entry");
  		*(int *)io++ = pte++->pg_pfnum | PG_V;

--- 417,419 -----
  			if (pte->pg_pfnum == 0)
! 				panic("mba zero entry");
  			*(int *)io++ = pte++->pg_pfnum | PG_V;
***************
*** 414,416
  	}
! 	*(int *)io++ = 0;
  	return (o);

--- 420,423 -----
  		}
! 		*(int *)io = 0;
! 	}
  	return (o);
RCS file: RCS/mt.c,v
retrieving revision 1.1
diff -b -c1 -r1.1 mt.c
*** /tmp/,RCSt1003216	Fri Jan  4 23:02:04 1985
--- mt.c	Wed Jan  2 12:22:07 1985
*************** Lots of these changes are stuff I never heard of.  The
*************** only mass driver change is the addition of mtmass().
*************** You can install these or not, as you choose.
*** 9,10
   *
   * TODO:

--- 9,15 -----
   *
+  * Changed by Jules P. Aronson to correct
+  * faulty identification of logical unit numbers with slave numbers
+  * specifically, the construct MUUNIT(dev) is replaced with sc->sc_slave
+  * in most cases.
+  *
   * TODO:
***************
*** 19,21
  #include "../h/buf.h"
- #include "../h/conf.h"
  #include "../h/dir.h"

--- 24,25 -----
  #include "../h/buf.h"
  #include "../h/dir.h"
***************
*** 23,24
  #include "../h/user.h"
  #include "../h/map.h"

--- 27,29 -----
  #include "../h/user.h"
+ #include "../h/proc.h"
  #include "../h/map.h"
***************
*** 53,54
  #define MTUNIT(dev)	(mutomt[MUUNIT(dev)])
  

--- 58,60 -----
  #define MTUNIT(dev)	(mutomt[MUUNIT(dev)])
+ #define MUSLAVE(dev)	((&mu_softc[MUUNIT(dev)])->sc_slave)
  
***************
*** 68,69
  } mu_softc[NMU];
  short	mutomt[NMU];

--- 74,76 -----
  } mu_softc[NMU];
+ 
  short	mutomt[NMU];
***************
*** 119,122
  	muunit = MUUNIT(dev);
! 	if (muunit >= NMU || (sc = &mu_softc[muunit])->sc_openf ||
! 	    (mi = mtinfo[MTUNIT(dev)]) == 0 || mi->mi_alive == 0)
  		return (ENXIO);

--- 126,128 -----
  	muunit = MUUNIT(dev);
! 	if (muunit >= NMU || (mi = mtinfo[MTUNIT(dev)]) == 0 || !mi->mi_alive)
  		return (ENXIO);
***************
*** 122,123
  		return (ENXIO);
  	olddens = sc->sc_dens;

--- 128,132 -----
  		return (ENXIO);
+ 	sc = &mu_softc[muunit];
+ 	if (sc->sc_openf)
+ 		return (EBUSY);
  	olddens = sc->sc_dens;
***************
*** 239,241
  	} else {
! 		mtaddr->mtncs[MUUNIT(bp->b_dev)] =
  			(bp->b_repcnt<<8)|bp->b_command|MT_GO;

--- 248,250 -----
  	} else {
! 		mtaddr->mtncs[sc->sc_slave] =
  			(bp->b_repcnt<<8)|bp->b_command|MT_GO;
***************
*** 245,247
  		if (mi->mi_tab.b_errcnt == 2) {
! 			mtaddr->mtca = MUUNIT(bp->b_dev);
  		} else {

--- 254,256 -----
  		if (mi->mi_tab.b_errcnt == 2) {
! 			mtaddr->mtca = sc->sc_slave;
  		} else {
***************
*** 248,250
  			mtaddr->mtbc = bp->b_bcount;
! 			mtaddr->mtca = (1<<2)|MUUNIT(bp->b_dev);
  		}

--- 257,259 -----
  			mtaddr->mtbc = bp->b_bcount;
! 			mtaddr->mtca = (1<<2)|sc->sc_slave;
  		}
***************
*** 253,255
  	if (blkno < bdbtofsb(bp->b_blkno))
! 		mtaddr->mtncs[MUUNIT(bp->b_dev)] =
  		  (min((unsigned)(bdbtofsb(bp->b_blkno) - blkno), 0377) << 8) |

--- 262,264 -----
  	if (blkno < bdbtofsb(bp->b_blkno))
! 		mtaddr->mtncs[sc->sc_slave] =
  		  (min((unsigned)(bdbtofsb(bp->b_blkno) - blkno), 0377) << 8) |
***************
*** 257,259
  	else
! 		mtaddr->mtncs[MUUNIT(bp->b_dev)] =
  		  (min((unsigned)(blkno - bdbtofsb(bp->b_blkno)), 0377) << 8) |

--- 266,268 -----
  	else
! 		mtaddr->mtncs[sc->sc_slave] =
  		  (min((unsigned)(blkno - bdbtofsb(bp->b_blkno)), 0377) << 8) |
***************
*** 284,286
  	register struct buf *bp = mi->mi_tab.b_actf;
! 	register struct mu_softc *sc;
  

--- 293,295 -----
  	register struct buf *bp = mi->mi_tab.b_actf;
! 	register struct mu_softc *sc = &mu_softc[MUUNIT(bp->b_dev)];
  
***************
*** 287,289
  	/* I'M NOT SURE IF THIS SHOULD ALWAYS BE THE CASE SO FOR NOW... */
! 	if ((mtaddr->mtca&3) != MUUNIT(bp->b_dev)) {
  		printf("mt: wrong unit!\n");

--- 296,298 -----
  	/* I'M NOT SURE IF THIS SHOULD ALWAYS BE THE CASE SO FOR NOW... */
! 	if ((mtaddr->mtca&3) != sc->sc_slave) {
  		printf("mt: wrong unit!\n");
***************
*** 289,291
  		printf("mt: wrong unit!\n");
! 		mtaddr->mtca = MUUNIT(bp->b_dev);
  	}

--- 298,300 -----
  		printf("mt: wrong unit!\n");
! 		mtaddr->mtca = sc->sc_slave;
  	}
***************
*** 291,293
  	}
- 	sc = &mu_softc[MUUNIT(bp->b_dev)];
  	sc->sc_erreg = mtaddr->mter;

--- 300,301 -----
  	}
  	sc->sc_erreg = mtaddr->mter;
***************
*** 369,371
  	register struct mu_softc *sc;
! 	int er, fc, unit;
  

--- 377,379 -----
  	register struct mu_softc *sc;
! 	int er, fc, slave;
  
***************
*** 371,373
  
! 	unit = (mtaddr->mtner >> 8) & 3;
  	er = MASKREG(mtaddr->mtner);

--- 379,381 -----
  
! 	slave = (mtaddr->mtner >> 8) & 3;
  	er = MASKREG(mtaddr->mtner);
***************
*** 374,376
  	/* WILL THIS OCCUR IF ANOTHER DRIVE COMES ONLINE? */
! 	if (bp == 0 || unit != MUUNIT(bp->b_dev)) {	/* consistency check */
  		if ((er & MTER_INTCODE) != MTER_ONLINE)

--- 382,387 -----
  	/* WILL THIS OCCUR IF ANOTHER DRIVE COMES ONLINE? */
! 	if (bp == NULL)
! 		return (MBN_SKIP);
! 	sc = &mu_softc[MUUNIT(bp->b_dev)];
! 	if (slave != sc->sc_slave) {	/* consistency check */
  		if ((er & MTER_INTCODE) != MTER_ONLINE)
***************
*** 376,378
  		if ((er & MTER_INTCODE) != MTER_ONLINE)
! 			printf("mt: unit %d random interrupt\n", unit);
  		return (MBN_SKIP);

--- 387,390 -----
  		if ((er & MTER_INTCODE) != MTER_ONLINE)
! 			printf("mt: unit %d random interrupt\n",
! 					MUUNIT(bp->b_dev));
  		return (MBN_SKIP);
***************
*** 379,384
  	}
! 	if (bp == 0)
! 		return (MBN_SKIP);
! 	fc = (mtaddr->mtncs[unit] >> 8) & 0xff;
! 	sc = &mu_softc[unit];
  	sc->sc_erreg = er;

--- 391,393 -----
  	}
! 	fc = (mtaddr->mtncs[slave] >> 8) & 0xff;
  	sc->sc_erreg = er;
*************** Here's the mass driver addition:
*** 492,493
  	return (0);
  }

--- 501,517 -----
  	return (0);
+ }
+ 
+ /*
+  * Start a ``raw block transfer'' from kernel space (via the
+  * mass driver).
+  */
+ mtmass(bp)
+ 	register struct buf *bp;
+ {
+ 	register struct mu_softc *sc;
+ 
+ 	minphys(bp);
+ 	sc = &mu_softc[MUUNIT(bp->b_dev)];
+ 	sc->sc_blkno = bdbtofsb(bp->b_blkno);
+ 	sc->sc_nxrec = sc->sc_blkno + 1;
  }
RCS file: RCS/param.h,v
retrieving revision 1.1
diff -b -c1 -r1.1 param.h
*** /tmp/,RCSt1003120	Fri Jan  4 22:59:49 1985
--- param.h	Wed Jan  2 09:52:11 1985
*************** We like more processes and more files per process; this
*************** has nothing to do with the mass driver.
*** 18,21
  #define	MSWAPX	15		/* pseudo mount table index for swapdev */
! #define	MAXUPRC	25		/* max processes per user */
! #define	NOFILE	20		/* max open files per process */
  /* NOFILE MUST NOT BE >= 31; SEE pte.h */

--- 18,21 -----
  #define	MSWAPX	15		/* pseudo mount table index for swapdev */
! #define	MAXUPRC	40		/* max processes per user */
! #define	NOFILE	30		/* max open files per process (std=20) */
  /* NOFILE MUST NOT BE >= 31; SEE pte.h */
*************** We like more groups too.  I recall a problem with UPAGES
*************** getting too big if you increase NGROUPS, so you might want
*************** to avoid this.
*** 23,25
  #define	NCARGS	10240		/* # characters in exec arglist */
! #define	NGROUPS	8		/* max number groups */
  

--- 23,25 -----
  #define	NCARGS	10240		/* # characters in exec arglist */
! #define	NGROUPS	16		/* max number groups (std=8)*/
  
*************** I have most of the clist routines in VAX assembly, so I
*************** moved the definitions to a separate file.  You probably
*************** don't want to do this.
*** 97,100
  
! #define	CBSIZE	28		/* number of chars in a clist block */
! #define	CROUND	0x1F		/* clist rounding; sizeof(int *) + CBSIZE -1*/
  

--- 97,103 -----
  
! #ifndef KERNEL
! #include <sys/cblock.h>
! #else
! #include "../h/cblock.h"
! #endif
  
*************** Fixing some comments...
*** 116,118
   * file systems; however making it smaller make make some file
!  * systems unmountable.
   *

--- 119,122 -----
   * file systems; however making it smaller make make some file
!  * systems unmountable.  Also, note that MAXBSIZE must be >= MINBSIZE
!  * (defined in ../h/fs.h).
   *
*************** More comments, related to the way things are after adding
*************** the mass driver.  Install these if you are installing the
*************** mass driver.
*** 118,119
   *
   * Note that the blocked devices are assumed to have DEV_BSIZE

--- 122,134 -----
   *
+  * MAXBSIZE is probably much larger than the average file system
+  * block, and it makes more sense to allocate buffers and memory
+  * based on the average size than on the maximum size.  AVGBSIZE
+  * should be set to the "average" block size on your file systems.
+  * Setting it too small will mean that buffer memory will be scarce,
+  * while setting it too large will mean that buffers themselves will
+  * be the scarce resource.
+  *
+  * N.B.: MAXBSIZE must be a multiple of CLBYTES.  Also, if you
+  * increase MAXBSIZE, you may have to increase SYSPTSIZE.
+  *
   * Note that the blocked devices are assumed to have DEV_BSIZE
*************** The change to MAXBSIZE and the definition of AVGBSIZE
*************** for the new code in vax/machdep.c.  (I.e., the former
*************** goes with the latter.)
*** 127,129
   */
! #define	MAXBSIZE	8192
  #define	DEV_BSIZE	512

--- 142,145 -----
   */
! #define	MAXBSIZE	16384		/* was 8192 */
! #define	AVGBSIZE	4096
  #define	DEV_BSIZE	512
RCS file: RCS/ts.c,v
retrieving revision 1.1
diff -b -c1 -r1.1 ts.c
*** /tmp/,RCSt1003186	Fri Jan  4 23:01:16 1985
--- ts.c	Wed Jan  2 11:57:07 1985
*************** Yet another useless conf.h
*** 16,18
  #include "../h/dir.h"
- #include "../h/conf.h"
  #include "../h/user.h"

--- 16,17 -----
  #include "../h/dir.h"
  #include "../h/user.h"
*************** Yet another BRL csleep change to ignore
*** 296,298
  		bp->b_flags |= B_WANTED;
! 		sleep((caddr_t)bp, PRIBIO);
  	}

--- 295,297 -----
  		bp->b_flags |= B_WANTED;
! 		csleep((caddr_t)bp, PRIBIO, "TAPE");
  	}
*************** I like type names.
*** 509,511
  	int tsunit;
! 	register state;
  

--- 508,510 -----
  	int tsunit;
! 	register int state;
  
*************** This is necessary for the mass driver 'cause we need to
*************** know the state ``inside'' biodone()... biodone may wind
*************** up invoking the strategy routine, and we don't want it
*************** calling tsstart() if the tape is really busy.
*** 533,535
  	state = um->um_tab.b_active;
- 	um->um_tab.b_active = 0;
  	/*

--- 532,533 -----
  	state = um->um_tab.b_active;
  	/*
*************** Formatting, formatting!  Sheesh.
*** 575,577
  			 */
! 			if (state==SIO) {
  				if (++um->um_tab.b_errcnt < 7) {

--- 573,575 -----
  			 */
! 			if (state == SIO) {
  				if (++um->um_tab.b_errcnt < 7) {
*************** Better state info.
*** 660,662
  	iodone(bp);
! 	if (um->um_tab.b_actf->b_actf == 0)
  		return;

--- 658,661 -----
  	iodone(bp);
! 	if (um->um_tab.b_actf->b_actf == 0) {
! 		um->um_tab.b_active = 0;
  		return;
***************
*** 662,663
  		return;
  opcont:

--- 661,663 -----
  		return;
+ 	}
  opcont:
*************** The mass driver entry point.
*** 733,734
  	return (0);
  }

--- 733,750 -----
  	return (0);
+ }
+ 
+ /*
+  * Start a ``raw block transfer'' from kernel space (via the
+  * mass driver).  Job is a combination minphys() and part of
+  * tsphys().
+  */
+ tsmass(bp)
+ 	register struct buf *bp;
+ {
+ 	register struct ts_softc *sc;
+ 
+ 	minphys(bp);
+ 	sc = &ts_softc[TSUNIT(bp->b_dev)];
+ 	sc->sc_blkno = bdbtofsb(bp->b_blkno);
+ 	sc->sc_nxrec = sc->sc_blkno + 1;
  }
RCS file: RCS/ufs_machdep.c,v
retrieving revision 1.1
diff -b -c1 -r1.1 ufs_machdep.c
*** /tmp/,RCSt1003161	Fri Jan  4 23:00:57 1985
--- ufs_machdep.c	Wed Jan  2 10:42:31 1985
*************** Still another useless conf.h!  Sigh.
*** 9,11
  #include "../h/buf.h"
- #include "../h/conf.h"
  #include "../h/proc.h"

--- 9,10 -----
  #include "../h/buf.h"
  #include "../h/proc.h"
*************** Gripe if someone asks for more space than exists in any
*************** one buffer.
*** 30,31
  	sizealloc = roundup(size, CLBYTES);
  	/*

--- 29,33 -----
  	sizealloc = roundup(size, CLBYTES);
+ 	if (sizealloc > MAXBSIZE)
+ 		panic("allocbuf");
+ 
  	/*
*************** Minor style thingy.  I prefer parallel construction...
*** 69,71
  		tp->b_bufsize += take;
! 		bp->b_bufsize = bp->b_bufsize - take;
  		if (bp->b_bcount > bp->b_bufsize)

--- 71,73 -----
  		tp->b_bufsize += take;
! 		bp->b_bufsize -= take;
  		if (bp->b_bcount > bp->b_bufsize)
RCS file: RCS/vm_mem.c,v
retrieving revision 1.1
diff -b -c1 -r1.1 vm_mem.c
*** /tmp/,RCSt1003130	Fri Jan  4 23:00:00 1985
--- vm_mem.c	Wed Jan  2 10:24:43 1985
*************** More BRL csleep stuff to ignore
*** 39,41
  		while (freemem == 0)
! 			sleep((caddr_t)&freemem, PSWP+2);
  		m = imin(size, freemem);

--- 39,41 -----
  		while (freemem == 0)
! 			csleep((caddr_t)&freemem, PSWP+2, "FREEMEM");
  		m = imin(size, freemem);
*************** The C-compiler-sign-extension-bug fix (1 of 2; the other
*************** is in some incredibly similar piece of code somewhere else
*************** that I don't particularly recall.)
*** 251,252
  			}
  			if (mfind(c->c_mdev == MSWAPX ?

--- 251,253 -----
  			}
+ #ifdef lint
  			if (mfind(c->c_mdev == MSWAPX ?
***************
*** 255,256
  				panic("memall mfind");
  			c1->c_mdev = 0;

--- 256,263 -----
  				panic("memall mfind");
+ #else			/* Avoid compiler sign extend bug */
+ 			if (mfind(c->c_mdev == MSWAPX ?
+ 			    swapdev : mount[c->c_mdev].m_dev,
+ 			    c->c_blkno))
+ 				panic("memall mfind");
+ #endif lint
  			c1->c_mdev = 0;
*************** A side dish of changes to make meminit complain about
*************** improperly sized core maps, and to (probably) make the
*************** generated code smaller too.
*** 518,520
  /*
!  * Initialize core map
   */

--- 525,528 -----
  /*
!  * Initialize core map.  Print a warning message if the core map is
!  * too small to map everything, or another if it's way too big.
   */
***************
*** 523,525
  {
! 	register int i;
  	register struct cmap *c;

--- 531,533 -----
  {
! 	register int i, lastcm;
  	register struct cmap *c;
***************
*** 530,535
  	ecmx = ecmap - cmap;
! 	if (ecmx < freemem / CLSIZE)
! 		freemem = ecmx * CLSIZE;
! 	for (i = 1; i <= freemem / CLSIZE; i++) {
! 		cmap[i-1].c_next = i;
  		c = &cmap[i];

--- 538,549 -----
  	ecmx = ecmap - cmap;
! 	lastcm = freemem / CLSIZE;
! 	if (ecmx < lastcm) {
! 		printf("core map %d too small (lost %d bytes of user mem)\n",
! 			lastcm - ecmx, freemem - ecmx * CLSIZE);
! 		lastcm = ecmx;
! 		freemem = lastcm * CLSIZE;
! 	}
! 	else if (ecmx - 20 > lastcm)	/* probably shouldn't happen */
! 		printf("%d unused core map entries\n", ecmx - lastcm);
! 	for (i = 1; i <= lastcm; i++) {
  		c = &cmap[i];
***************
*** 535,536
  		c = &cmap[i];
  		c->c_prev = i-1;

--- 549,551 -----
  		c = &cmap[i];
+ 		c[-1].c_next = i;
  		c->c_prev = i-1;
***************
*** 542,544
  	}
! 	cmap[freemem / CLSIZE].c_next = CMHEAD;
  	for (i = 0; i < CMHSIZ; i++)

--- 557,561 -----
  	}
! 	cmap[CMHEAD].c_prev = lastcm;
! 	cmap[CMHEAD].c_type = CSYS;
! 	cmap[lastcm].c_next = CMHEAD;
  	for (i = 0; i < CMHSIZ; i++)
***************
*** 545,548
  		cmhash[i] = ecmx;
- 	cmap[CMHEAD].c_prev = freemem / CLSIZE;
- 	cmap[CMHEAD].c_type = CSYS;
  	avefree = freemem;

--- 562,563 -----
  		cmhash[i] = ecmx;
  	avefree = freemem;
*************** Yet another BRL csleep to ignore
*** 577,579
  		c->c_want = 1;
! 		sleep((caddr_t)c, PSWP+1);
  	}

--- 592,594 -----
  		c->c_want = 1;
! 		csleep((caddr_t)c, PSWP+1, "PAGE");
  	}
RCS file: RCS/vmparam.h,v
retrieving revision 1.1
diff -b -c1 -r1.1 vmparam.h
*** /tmp/,RCSt1003173	Fri Jan  4 23:01:05 1985
--- vmparam.h	Wed Jan  2 10:43:06 1985
*************** We have bigger data and stack limits.  Ignore these unless
*************** you REALLY know what you're doing here.
*** 26,28
  #ifndef MAXDSIZ
! #define	MAXDSIZ		(12*1024-32-SLOP)	/* max data size (clicks) */
  #endif

--- 26,28 -----
  #ifndef MAXDSIZ
! #define	MAXDSIZ		(16*2048-32-SLOP)	/* max data size (clicks) */
  #endif
***************
*** 28,30
  #endif
! #define	MAXSSIZ		(12*1024-32-SLOP)	/* max stack size (clicks) */
  

--- 28,30 -----
  #endif
! #define	MAXSSIZ		(6*2048-32-SLOP)	/* max stack size (clicks) */
  
*************** Increase SYSPTSIZE to handle mapping those 16K buffers.
*************** The comment is right, but making it work that way is hard.
*** 34,36
  /* SYSPTSIZE IS SILLY; IT SHOULD BE COMPUTED AT BOOT TIME */
! #define	SYSPTSIZE	((20+MAXUSERS)*NPTEPG)
  #define	USRPTSIZE 	(8*NPTEPG)

--- 34,36 -----
  /* SYSPTSIZE IS SILLY; IT SHOULD BE COMPUTED AT BOOT TIME */
! #define	SYSPTSIZE	((40+MAXUSERS)*NPTEPG)
  #define	USRPTSIZE 	(8*NPTEPG)
*************** Not sure exactly what this does, but it affects the
*************** printf about "should use interleaved swap".
*** 114,116
   */
! #define	LOTSOFMEM	2
  

--- 114,116 -----
   */
! #define	LOTSOFMEM	3
  
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 kernel_mods
	/bin/echo -n '	'; /bin/ls -ld kernel_mods
fi
-- 
(This line accidently left nonblank.)

In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (301) 454-7690
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@maryland