Ultrix-3.1/src/cmd/olx/rax2.c

Compare this file to the similar file:
Show the results in this format:


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

static char Sccsid[] = "@(#)rax2.c	3.1	3/26/87";
/*
 * ULTRIX-11 UDA50 - RA60/RA80/RA81 disk exerciser program (rax).
 *	     KDA50 - RA60/RA80/RA81
 *	     RQDX1/2 - RD31/RD32/RD51/RD52/RD53/RD54/RX50/RX33
 *	     RQDX3 - RD31/RD32/RD51/RD52/RD53/RD54/RX50/RX33
 *	     RUX1  - RX50
 *	     KLESI - RC25
 *
 * Fred Canter
 * Bill Burns 
 *	added event flags	4/19/84
 *
 * PART 2 - (rax2.c)
 *
 *	Part 2 is the actual disk exerciser
 *
 *	Usage:
 *		rax rax_#.arg # #
 *		    ^	      ^ ^
 *		    |	      | event flag id
 *		    |	      event flag bit position
 *		    argument file name
 *
 *
 ****************************************************************
 *								*
 *	The last ## blocks of each drive are reserved		*
 *	as the maintenance area. Unless the [-w] option		*
 *	is specified, this is the only area where writes	*
 *	are allowed. If [-w] is specified, then the		*
 *	normal read only rules apply.				*
 *	## = 1000 for UDA50, 32 for RQDX1/3, 102 for KLESI	*
 *								*
 ****************************************************************
 *
 *	CHANGES FOR USER SETABLE DISK PARTITIONS -- Fred Canter 7/5/85
 *
 *	Note - most of this work already done in rax1.c (rax).
 *	The disk partition sizes table in the currently running kernel is
 *	compared with the standard sizes table, and the operation of RAX
 * 	is modified accordingly. The standard sizes table is hard coded
 *	into the RA exerciser via "#include /usr/sys/conf/dksizes.c".
 *
 *	If the sizes tables match, then RAX allows full functionality, i.e,
 *	it knows the disk layout and can protect the user from him/her self.
 *
 *	If the sizes tables don't match, it is assumed that the user changed
 *	the partition layout for some reason, and RAX operation is modified
 *	as follows:
 *
 *	1.	If the user changed partition 7, fatal error exit.
 *
 *	2.	If any partition, other than 7, overlaps the maintenance
 *		area, fatal error exit.
 *
 *	3.	If the disk is the system disk (root, swap, error log) or
 *		has any mounted file systems, then RAX will only write in
 *		the maintenance area. The rest of the disk is read only.
 *
 *	4.	If the disk is not the system disk and has no mounted file
 *		systems, then partition 7 will be a free fire zone. Only
 *		partition 7 will be used.
 *
 *	5.	The -f flag is ignored.
 *
 *	Note -	Absence of the -w flag will also limit access to the 
 *		maintenance area.
 *
 */

#include <sys/param.h>
#include <sys/devmaj.h>
#include <sys/ra_info.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <errno.h>

#define	NPASS	5000

/*
 * File system layout, also
 * obtained from the ULTRIX-11 kernel.
 */

int	nsdp;		/* non standard partitions layout in use */
int	dpmask;		/* partitions used by this type of disk */
			/* bitwise 1 = partition used */
struct	rasize	rasizes[8];
int	ra_ctid;
int	ra_mas;

/*
 * Unit number.
 */

int	cn;
int	dn;

struct	ra_drv	ra_drv[8];

/*
 * Selected file system array.
 * Element = 0 for file system not selected.
 * Element = 1 for file system selected.
 */

char fsact[] {0, 0, 0, 0, 0, 0, 0, 0};

/*
 * File system write read status array.
 * element is zero for write/read access
 * element is non zero for read only access
 */

char	fswrs[8];

/*
 * File descriptors for read/write and random read.
 * The first 8 file descriptors are for block I/O
 * and the second 8 are for raw I/O.
 * -1,   file system not open
 * >= 0, file system is open
 */

int	fd_rw[16];

/*
 * write, read, error statistics.
 */

long	rdcnt;
long	wrtcnt;
long	hecnt;

/*
 * block and raw I/O file name.
 */

char	fn[20];

/*
 * The following three lines of code
 * are the interface to the system call
 * error return messages.
 */

int	errno;
int	sys_nerr;
char	*sys_errlist[];

/*
 * Time buffers.
 */

int	istime;
time_t	btbuf;	/* Exerciser run beginning time */
time_t	etbuf;	/* Exerciser run ending time */
time_t	stbuf;	/* I/O statistics time */
time_t	timbuf;
struct tm *tl;	/* structure for localtime */
char	btime[13];	/* ascii beg time yymmddhhmmss */
char	etime[13];	/* ascii end time yymmddhhmmss */

char	*diskn;		/* Disk type name */
char	diskdn[] = "-ra#";

/*
 * Write/read buffers,
 * size depends on amount of free memory.
 * The buffer size is 8k words for > 256 kb
 * and 4k words for < 256 kb.
 * Buffer area obtained via calloc();
 */

int	*wbuf;	/* points to write buffer */
int	*rbuf;	/* points to read buffer */
int	bufsiz;
int	bcmask;

long	randx;

/*
 * Structure for selecting test data patterns.
 * Patterns were lifted from the UDA diagnostic.
 */

#define	P_RAND	0
#define	P_SEQ1	1
#define	P_SEQ3	2
#define	P_SEQ16	3

struct	{
	int	p_type;		/* type of data pattern */
	int	p_data[16];	/* actual data pattern */
} pat[16] =  {
	{ P_RAND },
	{ P_SEQ1, 0105613 },
	{ P_SEQ1, 031463 },
	{ P_SEQ1, 030221 },
	{ P_SEQ16, 1, 3, 7, 017, 037, 077, 0177, 0377, 0777, 01777,
		03777, 07777, 017777, 037777, 077777, 0177777 },
	{ P_SEQ16, 0177776, 0177774, 0177770, 0177760, 0177740, 0177700,
		0177600, 0177400, 0177000, 0176000, 0174000, 0170000,
		0160000, 0140000, 0100000, 0 },
	{ P_SEQ16, 0, 0, 0, -1, -1, -1, 0, 0, -1, -1, 0, -1, 0, -1, 0, -1 },
	{ P_SEQ1, 0133331 },
	{ P_SEQ16, 052525, 052525, 052525, 0125252, 0125252,
		0125252, 052525, 052525, 0125252, 0125252,
		052525, 0125252, 052525, 0125252, 052525, 0125252 },
	{ P_SEQ1, 0155554 },
	{ P_SEQ16, 026455, 026455, 026455, 0151322, 0151322,
		0151322, 026455, 026455, 0151322, 0151322,
		026455, 0151322, 026455, 0151322, 026455, 0151322 },
	{ P_SEQ1, 066666 },
	{ P_SEQ16, 1, 2, 4, 010, 020, 040, 0100, 0200, 0400, 01000,
		02000, 04000, 010000, 020000, 040000, 0100000 },
	{ P_SEQ16, 0177776, 0177775, 0177773, 0177767, 0177757,
		0177737, 0177677, 0177577, 0177377, 0176777,
		0175777, 0173777, 0167777, 0157777, 0137777, 077777 },
	{ P_SEQ3, 0155555, 0133333, 0155555 },
	{ P_SEQ16, 0133331, 0133331, 0133331, 0155554, 0155554,
		0155554, 0133331, 0133331, 0155554, 0155554,
		0133331, 0155554, 0133331, 0155554, 0133331, 0155554 },
};

#ifdef EFLG
#include <sys/eflg.h>
char	*efpis;
char	*efids;
int	efbit;
int	efid;
long	evntflg();
int	zflag;
#else
char	*killfn;
#endif

int	ndep, ndrop, wflag;

int	rbc;	/* byte count returned from read/write calls */

int	rootdev;

main(argc, argv)
char *argv[];
int argc;
{
	register int	*wbp, *rbp;
	register int	ctr;
	int	stop(), intr();
	FILE	*argf;
	int fflag, sflag;
	int i, j, k;
	int fd;
	int *ap;
	char *p, *n;
	int a, b;
	int	rderr, count, cnt, nbytes;
	int	w_off, r_off, rr_off;
	int	fsterr, feb, neb;
	int	nwlb, rba, nw;
	daddr_t	ebn, bn;
	int	nfb, rbn;
	int	ebc, woff, roff;
	daddr_t	bn_rw, bn_rr;
	int	fs_rw, fs_rr, efd;
	int	ronly;
	long	fsz, lbt;

	signal(SIGINT, SIG_IGN);
	signal(SIGTERM, intr);
	signal(SIGQUIT, stop);
	close(stdin);
	if((argc < 2) || (argc > 4))
		exit(1);
/*
 * Read needed date from the `rax_#.arg' file (# = drive).
 */
	argf = fopen(argv[1], "r");
	if(argf == NULL)
		exit(1);
	cn = getw(argf);
	dn = getw(argf);
	ndep = getw(argf);
	ndrop = getw(argf);
	fflag = getw(argf);
	sflag = getw(argf);
	wflag = getw(argf);
	istime = getw(argf);
	ronly = getw(argf);
	bufsiz = getw(argf);
	bcmask = getw(argf);
	ra_mas = getw(argf);
	ra_ctid = getw(argf);
	nsdp = getw(argf);
	dpmask = getw(argf);
#ifdef EFLG
	zflag = getw(argf);
#endif
	n = &rasizes;
	for(i=0; i<sizeof(rasizes); i++)
		*n++ = getc(argf);
	n = &ra_drv;
	for(i=0; i<sizeof(ra_drv); i++)
		*n++ = getc(argf);
	for(i=0; i<sizeof(fsact); i++)
		fsact[i] = getc(argf);
	for(i=0; i<sizeof(fswrs); i++)
		fswrs[i] = getc(argf);
	fclose(argf);
	unlink(argv[1]);
#ifdef EFLG
	if(zflag) {
		efpis = argv[2];
		efids = argv[3];
	}
#else
	killfn = argv[2];	/* run/stop control file name */
#endif
/*
 * If this is the system disk, do not allow any
 * block mode I/O other than random reads.
 * Any disk with read only file systems !
 * This is because bdflush() will interfere with
 * normal buffer cache operations.
 */
	for(i=0; i<8; i++)
		if(fswrs[i] != 0)
			rootdev = 1;

/*
 * The EXERCISE mode exercises all selected file systems
 * on each selected drive by writing, reading , and
 * comparing the data.
 * A random read of a single block is also done.
 * The root, swap , error log , and any mounted file
 * systems are treated as read only.
 * The disk activity occurs on a four count cycle, i.e.,
 *
 *	count	io mode	 data	byte count
 *
 *	0	block	pat[?]	512
 *	1	block	pat[?]	random, 2048 max
 *	2	raw	pat[?]	512
 *	3	raw	pat[?]	8 count cycle
 *
 *	8 count cycle
 *
 *	random byte count - 8190 max,
 *	1024, 2048, 3072, 4096, 5120, 6144, 7168 bytes
 *
 *
 * Each count consists of the following sequence:
 *
 *  1.	Randumly select the file system and block number
 *	for the write/read/compare and the random read.
 *
 *  2.	Open the selected file systems if necessary.
 *
 *  3.	Select the data pattern to be used,
 *	from the pattern structure pat[].
 *
 *  4.	Load the write buffer with a data pattern.
 *
 *  5.	Write to the disk.
 *
 *  6.	Read a randomly selected block from the disk.
 *
 *  7.	Clear the read buffer.
 *
 *  8.	Read the data written in step 5. from the disk.
 *
 *  9.	Compare the data in the read buffer with the
 *	data pattern and report any mismatches.
 *
 * 10.	Increment the count and goto step one.
 */

/*
 * Allocate memory for Write/Read buffers.
 */
	wbuf = calloc(bufsiz, sizeof(int));
	rbuf = calloc(bufsiz, sizeof(int));
	if(wbuf == NULL || rbuf == NULL) {
		fprintf(stderr, "\nrax: Can't calloc R/W buffers\n");
		exit(1);
		}

/*
 * Set all file descriptors to -1 to
 * indicate all files closed.
 */
	for(i=0; i<16; i++)
		fd_rw[i] = -1;
	time(&btbuf);
	randx = btbuf & 0777;	/* initialize random number generator */
	switch(ra_drv[dn].ra_dt) {
	case RC25:
		diskn = "RC25";
		break;
	case RX33:
		diskn = "RX33";
		break;
	case RX50:
		diskn = "RX50";
		break;
	case RD31:
		diskn = "RD31";
		break;
	case RD32:
		diskn = "RD32";
		break;
	case RD51:
		diskn = "RD51";
		break;
	case RD52:
		diskn = "RD52";
		break;
	case RD53:
		diskn = "RD53";
		break;
	case RD54:
		diskn = "RD54";
		break;
	case RA60:
		diskn = "RA60";
		break;
	case RA80:
		diskn = "RA80";
		break;
	case RA81:
		diskn = "RA81";
		break;
	}
	printf("\n\n%s disk exerciser started - %s",diskn,ctime(&btbuf));
#ifdef EFLG
	if(zflag) {
		efbit = atoi(efpis);
		efid = atoi(efids);
		evntflg(EFCLR, efid, (long)efbit);
	}
#else
	unlink(killfn);
#endif
	time(&stbuf);
	fflush(stdout);
	nice(0);
/*
 * If this is the system disk, i.e.,
 * if the root and/or swap are on this drive,
 * run at lower priority.
 * This is done because the system disk already
 * gets a certain amount of activity even without
 * the exerciser running.
 */

/* No!
	for(i=0; i<8; i++)
		if((fswrs[i] == 1) || (fswrs[i] == 2)) {
			nice(20);
			break;
			}
*/
e_loop:
	cnt = count & 03;
	if(rootdev && ((cnt == 0) || (cnt == 1)))
		goto next;
	if(cnt & 1) {
		nbytes = rng() & bcmask;
		if(cnt == 1) {
			nbytes =& 03776;
			nbytes =+ 2;
		} else {
			a = (count >> 2) & 07;
			b = a * 1024;
			if(a)
				nbytes = b;
			if(nbytes < 4)
				nbytes = 8192;
			}
		}
	else
		nbytes = 512;
	rr_off = (count & 017) * 256;
	w_off = offset(nbytes);
	r_off = offset(nbytes);
/*
 * Generate a random file system, unless the [-f#]
 * option is specified. In that case use the 
 * first selected file system.
 * Also generate a random block number, check to insure
 * that it is within the bounds of the file system,
 * and adjust it if neccessary.
 */
	if(nsdp) {		/* non standard disk partitions in use */
		fs_rw = fsok(dn, 7);	/* use only file system 7 */
		fs_rr = fs_rw;		/* & possibly maint. area */
	} else if(fflag) {
		for(fs_rw=0; fsact[fs_rw]==0; fs_rw++)
		{}
		fs_rr = fs_rw;
		if(fs_rw != fsok(dn, fs_rw)) {
			fprintf(stderr,"\nrax: invalid file system selected\n");
			exit(1);
		}
	} else if(!ronly) {
		fs_rw = fsok(dn, 7);
		fs_rr = fs_rw;
	} else {
		fs_rw = (rng() >> 8) & 07;
		fs_rr = fsok(dn, 7);
		fs_rw = fsok(dn, fs_rw);
	}
	if(wflag == 0) {	/* write maint area only */
		fsz = rasizes[7].nblocks;
		fs_rw = 7;
		bn_rw = rasizes[7].nblocks - (long)ra_mas;
		if((ra_ctid == RQDX1) || (ra_ctid == RQDX3) || (ra_ctid == RUX1))
			bn_rw += (rng() & 037);
		else if(ra_ctid == KLESI)
			bn_rw += (rng() & 077);
		else
			bn_rw += (rng() & 01777);
	} else {
		fsz = rasizes[fs_rw].nblocks;
		bn_rw = (fsz / 8) * (rng() & 7);
		bn_rw += (unsigned int)rng();
	}
	lbt = bn_rw + ((nbytes + 511) / 512);
	if(lbt > fsz)
		bn_rw -= (lbt - fsz);
	fsz = rasizes[fs_rr].nblocks;
	switch(ra_ctid) {
	case KLESI:
		bn_rr = (unsigned int)rng() & 037777;
		lbt = bn_rr + ((nbytes + 511) / 512);
		if(lbt > fsz)
			bn_rr -= (lbt - fsz);
		break;
	case UDA50:
	case UDA50A:
	case KDA50:
		bn_rr = (unsigned int)rng();	/* bias toward lower numbers */
		lbt = bn_rr + ((nbytes + 511) / 512);
		if(lbt > fsz)
			bn_rr -= (lbt - fsz);
		break;
	case RUX1:
	case RQDX1:
	case RQDX3:
		/*
		 * TODO: Should check unit size and adjust bn_rr mask
		 *	 according to media type (size=800 for rx50
		 *	 size=2400 for rx33. RX33 drive can access both
		 *	 RX33 and RX50 media.
		 */
		if((ra_drv[dn].ra_dt == RX50) || (ra_drv[dn].ra_dt == RX33))
			bn_rr = (unsigned int)rng() & 0777;
		else {
			bn_rr = (unsigned int)rng() & 037777;
			lbt = bn_rr + ((nbytes + 511) / 512);
			if(lbt > fsz)
				bn_rr -= (lbt - fsz);
		}
		break;
	}
/*
 * Set the file descriptor (efd) for
 * the correct I/O mode, i.e., block or raw.
 */
	a = count & 02;
	if(a) {
		if((efd = fsopen(dn, fs_rw+8)) < 0) {	/* raw I/O */
		opnerr:
			fprintf(stderr,"\nrax: Can't open %s\n", fn);
		fatal:
			fprintf(stderr,"\nrax: FATAL ERROR, unit %d dropped !\n", dn);
			fflush(stderr);
			for( ;; )
				sleep(3600);
			}
	} else
		if((efd = fsopen(dn, fs_rw)) < 0)	/* block I/O */
			goto opnerr;
/*
 * Open the file system for 
 * the random read.
 */

	if(fsopen(dn, fs_rr) < 0)
		goto opnerr;
/*
 * Select the data pattern and load the write
 * buffer.
 * The random data pattern is a random number
 * which changes for each sector written.
 * The other patterns are selected from
 * the pattern structure.
 */
	wbp = wbuf;
	wbp += w_off;
	j = nbytes/2;
	k = rng() & 017;		/* select pattern type */
	switch(pat[k].p_type) {
	case P_RAND:			/* random data */
		for(ctr=0; ctr<j; ctr++) {
			if((ctr & 0377) == 0)
				rbp = rng();
			*wbp++ = rbp;
		}
		break;
	case P_SEQ1:			/* single word sequence */
		for(ctr=0; ctr<j; ctr++)
			*wbp++ = pat[k].p_data[0];
		break;
	case P_SEQ3:			/* three word sequence */
		for(ctr=0; ctr<j; ctr++) {
			if((ctr % 3) == 0)
				i = 0;
			*wbp++ = pat[k].p_data[i++];
		}
		break;
	case P_SEQ16:			/* sixteen word sequence */
		for(ctr=0; ctr<j; ctr++)
			*wbp++ = pat[k].p_data[ctr&017];
		break;
	}
/*
 * Write to disk and count write operations.
 *(only if file system is write/read)
 */
e_erc:	/* Entry point for hard error xfer restart */
	if((fswrs[fs_rw] == 0) || (wflag == 0)) {
		lseek(efd, (long)(bn_rw * 512), 0);
		wrtcnt++;
		wbp = wbuf;
		wbp += w_off;
		if((rbc = write(efd, (char *)wbp, nbytes)) != nbytes)
			dskse((cnt | 010), dn, fs_rw, bn_rw, nbytes);
			if(errno == ENXIO)	/* unit went off-line */
				goto fatal;
		}
/*
 * Read a random block from disk
 * in block I/O mode.
 * Count the read operation.
 */
	lseek(fd_rw[fs_rr], (long)(bn_rr * 512), 0);
	rdcnt++;
	rbp = rbuf;
	rbp += rr_off;
	if((rbc = read(fd_rw[fs_rr], (char *)rbp, 512)) != 512) {
		dskse(0, dn, fs_rr, bn_rr, 512);
		printf("\n******\n");
		if(errno == ENXIO)	/* unit went off-line */
			goto fatal;
		}
/*
 * Real read from disk.
 *
 * The block number, byte count , & buffer offset
 * are buffered because they are changed durring a
 * hard read error continuation operation.
 */

		woff = w_off;
		roff = r_off;
		ebc = nbytes;
		ebn = bn_rw;
/*
 * Zero the portion of the read buffer
 * that will hold the next read data.
 * The read buffer pointer (rbp) and the loop counter
 * (ctr) are registers for speed !
 */
	rbp = rbuf;
	rbp += roff;
	for(ctr=0; ctr<(nbytes/2); ctr++)
		*rbp++ = 0;

		rderr = 0;
		lseek(efd, (long)(ebn * 512), 0);
		rdcnt++;
		rbp = rbuf;
		rbp += roff;
		if((rbc = read(efd, (char *)rbp, ebc)) != ebc) {
			dskse(cnt, dn, fs_rw, ebn, ebc);
			rderr++;
			if(errno == ENXIO)	/* unit went off-line */
				goto fatal;
			}
		fsterr = 1; /* first error flag */
		if((fswrs[fs_rw] == 0) || (wflag == 0))
		{	/* only check data on r/w file systems */
		nfb = ebc/512;	/* # of full blocks */
		nwlb = (ebc%512)/2;	/* # of words in last block */

		rbn = 0;		/* relative block # */
	e_dcl:
		if(rbn > nfb)
			goto e_dclend;	/* all blocks checked */
		if(rbn == nfb) {	/* last block ? */
			nw = nwlb;	/* yes, change of words */
			if(nw == 0)
				goto e_dclend; /* no partial block at end */
		} else
			nw = 256;	/* # words for full block */

/*
 * If a hard read error occurs on a multi block transfer, 
 * all blocks after the bad block will fail the data
 * compare test because the transfer aborts on a hard read error.
 * In order to insure that all blocks are checked the transfer
 * is restarted at bad block plus one and the data compare test
 * is completed. The block number, byte count and buffer address
 * are adjusted accordingly.
 */

		if(rderr && !fsterr) {
			bn_rw =+ rbn;
			w_off =+ (rbn*256);
			r_off =+ (rbn*256);
			nbytes =- (rbn*512);
			printf("\n******\n");
			goto e_erc;
			}
		feb = 1;	/* first error in block flag */
		neb = 0;	/* # of errors in block */
		wbp = wbuf;
		wbp += (woff + (rbn*256));
		rbp = rbuf;
		rbp += (roff + (rbn*256));
		for(ctr=0; ctr<nw; ctr++) {	/* check data in 1 block */
			if(*rbp++ != *wbp++) {	/* data compare */
				if(!rderr && fsterr) {
					time(&timbuf);
printf("\n\n******\nDATA MISMATCH WITHOUT I/O ERROR - %s", ctime(&timbuf));
	dskse((cnt|020), dn, fs_rw, ebn, ebc);
					}
				if(fsterr) {
	printf("\n\nWrite was from word %4.d of write buffer", woff);
	printf("\nRead  was to   word %4.d of read  buffer", roff);
					}
				if(feb) {
	printf("\n\nDATA COMPARE ERROR - BLOCK %D ",(ebn+rbn));
	printf("\n\nWrite buffer address = %4.d", woff+(rbn*256));
	printf("\nRead  buffer address = %4.d", roff+(rbn*256));
					feb = 0;
					}
				if(++neb > ndep) {
				printf("\n\n[error printout limit exceeded]");
					break;
					}
				printf("\n\nWORD = %d",ctr);
			printf("\nGOOD = %06.o",*(wbuf+woff+(rbn*256)+ctr));
			printf("\nBAD  = %06.o",*(rbuf+roff+(rbn*256)+ctr));
				fsterr = 0;
				}
			}
		if(hecnt >= ndrop) {
	    printf("\n\nTotal error limit exceeded, unit %d dropped !\n", dn);
			fflush(stdout);
			for( ;; )
				sleep(3600);
		}
		rbn++;
		goto e_dcl;
		}
	e_dclend:
		if(!fsterr) {
			printf("\n******\n");
			fflush(stdout);
		}
		if(rderr && fsterr) {
			printf("\n******\n");
			fflush(stdout);
		}
/*
 * If the last operation was in block mode,
 * cancel delayed write on any buffers assosiated
 * with this major/minor device so that they will
 * not be flushed out to the disk at some time later
 * and overwrite the following raw I/O test.
 */
	if((cnt == 0) || (cnt == 1))
		bdflush((RA_BMAJ<<8)|(cn<<6)|(dn<<3)|fs_rw);
next:
	if(++count >= NPASS) {
		count = 0;
		time(&timbuf);
	    printf("\n%s disk exerciser end of pass - %s",diskn,ctime(&timbuf));
		fflush(stdout);
		if((i = fork()) > 0)
			exit(0);
		if(i == -1)
			fprintf(stderr,"\nrax: Can't fork new copy of rax !\n");
	}
	if(sflag)
		goto e_loop;
	time(&timbuf);
	if(((timbuf - stbuf) / 60) < istime)
		goto e_loop;
	pios();	/* Print I/O statistics */
	goto e_loop;
}

/*
 * Randum number generator.
 */

rng()
{
	return(((randx = randx * 1103515245 + 12345) >> 16) & 0177777);
}

/*
 * Disk status error printout function.
 */

dskse(et, drv, fs, blk, bc)
daddr_t blk;
{

	hecnt++;
	if((et & 020) == 0) {
		time(&timbuf);
		printf("\n\n******\nHARD DISK ERROR - %s",ctime(&timbuf));
		printf("Returned byte count = %d (-1 = error)", rbc);
		printf("\nError type: ");
		if(errno < sys_nerr)
			printf("%s\n", sys_errlist[errno]);
		else
			printf("Unknown error\n");
		}
	printf("\nunit  filesys  block    xfer size  xfer type");
	printf("\n%d     %d       %6.D  %5.u bytes  ",drv,fs,blk,bc);
	if(et & 2)
		printf("RAW I/O ");
	else
		printf("BLOCK I/O ");
	if(et & 010)
		printf("WRITE\n******\n");
	else
		printf("READ\n");
}

/*
 * Print I/O statistics
 */

pios()
{

	time(&stbuf);
	randx = stbuf & 0777;
	printf("\n\nI/O statistics - %s",ctime(&stbuf));
	printf("\ndrive   write       read        hard");
	printf("\nnumber  operations  operations  errors\n");
	printf("\n%6.d  %10.D", dn, wrtcnt);
	printf("  %10.D  %6.D", rdcnt, hecnt);
	printf("\n");
	fflush(stdout);
}

intr()
{
	signal(SIGTERM, intr);
#ifdef EFLG
	if(zflag) {
		if(!checkflg())
			return;
	} else
		return;
#else
	if(access(killfn, 0) != 0)
		return;
#endif
	stop();
}

stop()
{
	register int	i;

	signal(SIGTERM, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	for(i=0; i<16; i++)
		close(fd_rw[i]);
	time(&etbuf);	/* ending time */
	printf("\n\n%s disk exerciser stopped - %s\n", diskn, ctime(&etbuf));
	pios();		/* print I/O stats */
	tconv(&btbuf, &btime);	/* convert beg time to ascii */
	tconv(&etbuf, &etime);	/* convert end time to ascii */
	fflush(stdout);
	diskdn[3] = dn + '0';
	if(fork() == 0)
	   execl("/bin/elp","elp","-s",diskdn,"-d",&btime,&etime,(char *)0);
	else
		while(wait() != -1) ;
#ifdef EFLG
	if(zflag)
		evntflg(EFCLR, efid, (long)efbit);
#else
	unlink(killfn);
#endif
	exit(0);
}

/*
 * This function returns a random write of read
 * buffer word offset, which assures
 * that the transfer will not overflow the buffer.
 * The buffer size (bufsiz) is based on free memory.
 */

offset(nb)
{
	register int	off, lim, wc;

	wc = nb/2;	/* convert byte count to word count */
	lim = bufsiz;	/* buffer size is xfer limit */
	off = rng() & (bufsiz - 2);	/* random offset */
	while((off + wc) > lim)	/* make xfer fit in buffer */
		off =- 256;
	if(off < 0)
		off = 0;
	return(off);
}

/*
 * Check to verify that the selected file system exists
 * on the selected drive type, adjust if necessary.
 */

fsok(dn, filsys)
{

	if((dpmask & (1 << filsys)) == 0)
		return(7);
	else
		return(filsys);
}

/*
 * File system open function.
 * Return a file descriptor for the
 * requested file system.
 * Open the file if necessary.
 *
 * dn	Disk drive number.
 * fso	File system to be opened.
 */

fsopen(dn, fso)
{

	register int j;

	j = fso & 07;
	if(fd_rw[fso] < 0) {
		switch(ra_drv[dn].ra_dt) {
		case RA60:
		case RA80:
		case RA81:
			if(fso < 8)			/* generate file name */
				sprintf(&fn, "/dev/ra%o%o", dn, j);
			else
				sprintf(&fn, "/dev/rra%o%o", dn, j);
			break;
		case RC25:
			if(fso < 8)			/* generate file name */
				sprintf(&fn, "/dev/rc%o%o", dn, j);
			else
				sprintf(&fn, "/dev/rrc%o%o", dn, j);
			break;
		case RD31:
		case RD32:
		case RD51:
		case RD52:
		case RD53:
		case RD54:
			if(fso < 8)			/* generate file name */
				sprintf(&fn, "/dev/rd%o%o", dn, j);
			else
				sprintf(&fn, "/dev/rrd%o%o", dn, j);
			break;
		case RX33:
		case RX50:
			if(fso < 8)			/* generate file name */
				sprintf(&fn, "/dev/rx%o", dn);
			else
				sprintf(&fn, "/dev/rrx%o", dn);
			break;
		}
		if((fswrs[j]==0) || (wflag==0))	/* open the file system */
			fd_rw[fso] = open(fn, 2);	/* write/read */
		else
			fd_rw[fso] = open(fn, 0);	/* read only */
		}
	return(fd_rw[fso]);
}

/*
 * This function converts the time from a time_t,
 * as returned by time(), to ascii in the form of
 * yymmddhhmmss.
 */

tconv(tim, timbuf)
time_t	*tim;
char	*timbuf;
{
	register int i;
	register char *p;
	int tb[6];

	tl = localtime(tim);
	tb[0] = tl->tm_year;
	tb[1] = tl->tm_mon + 1;
	tb[2] = tl->tm_mday;
	tb[3] = tl->tm_hour;
	tb[4] = tl->tm_min;
	tb[5] = tl->tm_sec;
	p = timbuf;
	for(i=0; i<6; i++) {
		*p++ = (tb[i]/10) + '0';
		*p++ = (tb[i]%10) + '0';
		}
	*p++ = 0;
}

/*
 * Check eventflags to stop
 * return 0 for continuation
 * return 1 to stop
 */
extern int errno;
checkflg()
{
	union efrt {
		long	efret;
		struct {
			int	a;
			int	b;
		} retval
	} ef;
	errno = 0;
	ef.efret = evntflg(EFRD, efid, (long)0);
	if(errno && ef.retval.a == -1) {
		zflag = 0;
		return(0);
	}
	if(ef.efret & (1L << efbit))
		return(1);
	return(0);
}