Subject: Re: disk verification/stress test?
To: Timm Wetzel <twetzel@gwdg.de>
From: Darrin B. Jewell <jewell@mit.edu>
List: port-next68k
Date: 07/22/1999 04:50:35
Timm Wetzel  <twetzel@gwdg.de> writes:
> Can anybody recommend some tools to verify/stress test a hard disk,
> preferably with NS3.3?

Well, I wrote a quick hack to stress test my next68k esp driver, and
have included it below.  (The esp driver doesn't pass it yet)

I have been using it on the raw scsi device as follows:

  # ./testio write 1234 2048000 /dev/rsd0d 
  # ./testio read  1234 2048000 /dev/rsd0d

Where "1234" is a random number seed which determines the repeatable
test io pattern to be performed and "2048000" is the size of the media
in 512 byte blocks.

This program will pick:
   . a random number of blocks to transfer
   . a random number of blocks to transfer per read/write
   . a random number of blocks to offset the transfer
   . a random byte alignment to use for reads/writes

And will then will open the file, seek to the offset and then write a
pseudo random test pattern or read and compare the results with the
expected pattern.

Darrin


/* testio.c */
/*
 * Copyright (c) 1999 Darrin B. Jewell
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Darrin B. Jewell
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

/* Size of a block: */
#define BLKSIZE 512

/* Maximum number of blocks allowed in a single io operation: */
#define MAXBLKS (1<<11)					/* 2048 */

int
main(int argc, char *argv[])
{
	int fd;
	int r;
	unsigned char *buf;
	unsigned char *iobuf;
	int total = 0;
	unsigned long seed;
	unsigned long blkcnt;
	const char *filename;
	unsigned long ioblen;
	unsigned long ioalgn;
	unsigned long iosize;
	unsigned long iooffs;
	int writeflag;

	if (argc != 5) {
		fprintf(stderr,"usage: %s read|write seed blkcnt filename\n",argv[0]);
		return(EXIT_FAILURE);
	}
	
	writeflag = !strcmp(argv[1],"write");
	seed = strtoul(argv[2],NULL,10);
	blkcnt = strtoul(argv[3],NULL,10);
	filename = argv[4];

	srandom(seed);
	iosize  = (random()%blkcnt)+1;  /* size of the region read/written, in blocks */
	iooffs  = random()%(blkcnt-iosize+1);	/* offset of the region read/written, in blocks */
	ioblen  = (random()%(MAXBLKS>iosize?iosize:MAXBLKS))+1;	/* number of blocks per read/write */
	ioalgn  = (random()%BLKSIZE);	  /* alignment of data in a block */

	/* reserve the first 100 values for this seed */
	{
		int i;
		for(i=4;i<100;i++) {
			random();
		}
	}


	fprintf(stdout,
			"seed   = %lu (0x%x)\tSpecified random number seed.\n"
			"blkcnt = %lu (0x%x)\tSpecified number of blocks available on media.\n"
			"ioblen = %lu (0x%x)\tBlocks per read or write operation.\n"
			"ioalgn = %lu (0x%x)\tByte alignment of data in a read or write operation.\n"
			"iosize = %lu (0x%x)\tBlocks to be read or written.\n"
			"iooffs = %lu (0x%x)\tBlocks offset from beginning of media.\n",
			seed,seed,
			blkcnt,blkcnt,
			ioblen,ioblen,
			ioalgn,ioalgn,
			iosize,iosize,
			iooffs,iooffs);
	fflush(stdout);

	{
		assert(ioalgn < ioblen*BLKSIZE);
		buf = malloc(ioblen*BLKSIZE+ioalgn);
		assert(buf);
		iobuf = buf+ioalgn-((unsigned long)buf%ioalgn);
	}

	fd = open(filename,O_RDWR|O_SYNC|O_RSYNC|O_DSYNC|O_CREAT, 0x777);
	if (fd == -1) {
		perror("open");
		return(EXIT_FAILURE);
	}
	fprintf(stdout,"open successful\n");

	{
		off_t or;
		or = lseek(fd,iooffs*BLKSIZE,SEEK_SET);
		if (or == -1) {
			perror("lseek");
			return(EXIT_FAILURE);
		}
		assert(or == iooffs*BLKSIZE);
	}
	fprintf(stdout,"lseek successful\n");

	{
		long i;
		for(i=0;i<(ioblen*BLKSIZE);i++) {
			iobuf[i] = 0xff;
		}
	}
	fprintf(stdout,"pages initialized\n");

	{
		double fraction = 0.0;

		do {
			if (writeflag) {
				long i;
				/* Seed the buffer with random values */
				for(i=0;i<(ioblen*BLKSIZE);i++) {
					iobuf[i] = random()%(UCHAR_MAX+1);
				}
				r = write(fd,iobuf,ioblen*BLKSIZE);
				if (r == -1) {
					perror("write");
					break;
				}
			} else {
				long i;
				/* Initialize buffer to 0 */
				for(i=0;i<(ioblen*BLKSIZE);i++) {
					iobuf[i] = 0;
				}
				r = read(fd,iobuf,ioblen*BLKSIZE);
				if (r == -1) {
					perror("read");
					break;
				}
				if (r != ioblen*BLKSIZE) {
					fprintf(stderr,"short read, got %d, expecting %d\n",r,ioblen*BLKSIZE);
					break;
				}
				/* compare the buffer with expected values */
				for(i=0;i<(ioblen*BLKSIZE);i++) {
					int rval;
					rval = random()%(UCHAR_MAX+1);
					if (iobuf[i] != rval) {
						fprintf(stderr,"Data mismatched at byte %d (0x%08x) got %d (0x%02x) expecting %d (0x%02x)\n",
								total+i,total+i,iobuf[i],iobuf[i],rval,rval);
						break;
					}
				}
				if (i!=(ioblen*BLKSIZE)) break;
			}
			assert(r >= 0);
			total += r;
			if (r != ioblen*BLKSIZE) {
				fprintf(stdout,"\n%s returned %d, total %d (0x%08x)\n",
						writeflag?"write":"read",r,total,total);
				break;
			} 
#if 1
			else {
				fprintf(stdout,".");
				fflush(stdout);
			}
#endif
#if 0
			{
				double frac = (total / (iosize *BLKSIZE));
				if (frac > (fraction+0.01)) {
					fprintf(stdout,"wrote %lu of %lu transfers, %f %%\n",
							total/(ioblen*BLKSIZE),
							iosize/ioblen,
							frac);
					fraction = frac;
				}
			}
#endif
		} while(r && (total < (iosize*BLKSIZE)));

	}

	r = close(fd);
	if (r == -1) {
		perror("close");
		return(EXIT_FAILURE);
	}
	fprintf(stdout,"close successful\n");

	free(buf);

	return(EXIT_SUCCESS);
}