Subject: Re: Disk testing?
To: None <port-alpha@NetBSD.ORG, xela@MIT.EDU>
From: Matthew Jacob <mjacob@feral.com>
List: port-alpha
Date: 02/24/1998 10:10:07
Bug reports and sneers to me.
/*
* $Id: diskex.c,v 1.8 1997/03/27 20:25:54 mjacob Exp $
*
* $Log: diskex.c,v $
* Revision 1.8 1997/03/27 20:25:54 mjacob
* cleanup, netbsd etc.
*
* Revision 1.7 1997/03/27 17:57:18 mjacob
* checkpoint
*
* Revision 1.6 1996/06/29 20:06:33 mjacob
* make some changes to clean up errors in randoms; limit to
* proper 2GB SEEK limit.
*
* Revision 1.5 1996/06/12 05:04:35 mjacob
* some convex stuff.
*
* Revision 1.4 1996/06/11 19:37:23 mjacob
* add in a SYSV define for solaris
*
* Revision 1.3 1995/07/26 22:01:27 mjacob
* make it work again w/o compile errros on sun.
* Always use gcc -O -Wall -D__USE_FIXED_PROTOYPES__
*
*
* Copyright (c) 1995-1996, Matthew Jacob
*
* This software is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; version 2.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* The author may be reached via electronic communications at
*
* mjacob@feral.com
*
* or, via United States Postal Address
*
* Matthew Jacob
* 1831 Castro Street
* San Francisco, CA, 94131
*/
#ifdef convex
#include <sys/types.h>
extern int optind;
extern int getopt(int, char **, const char *);
#define SEEK_T off64_t
#define SEEK lseek64
#define FSTAT fstat64
#define STAT_T stat64_t
#else
#define SEEK_T off_t
#define SEEK lseek
#define FSTAT fstat
#define STAT_T struct stat
#endif
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#ifdef sun
#ifdef __SVR4
#include <sys/dkio.h>
#else
#include <sun/dkio.h>
extern int gettimeofday(struct timeval *, struct timezone *);
extern void bzero(char *, int);
extern int strtol(const char *, char **, int);
#endif
extern int optind;
#endif
#ifdef linux
#include <sys/ioctl.h>
#include <linux/fs.h>
#endif
#ifdef convex
#include <sys/ioctl.h>
#include <interfaces/io_if/scsi/scsi.h>
#endif
#ifdef __NetBSD__
#include <sys/disklabel.h>
#include <sys/dkio.h>
static struct disklabel fred;
#endif
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
static int sequential = 1;
static int verbose = 1;
static int zflag = 0;
static int wpercent = 0;
static SEEK_T rfsize;
static SEEK_T fsize;
static SEEK_T seekbase;
static SEEK_T seeklim;
static SEEK_T cresize;
static unsigned long sampsize = 100;
static unsigned long blksize = 8192;
static char *filename = "diskex.data";
static char *buffer = NULL;
static SEEK_T szarg(char *);
static void operate(char *, int);
static char *usage =
"Usage:
diskex [-zqr] [-b bs] [-w pct] [-f filename] [-c size] [-s size] [-S sampsize]
-z (linux only) flush buffer caches every sampsize ops
(more closely approximates real device speeds)
-q non-verbose
-r random vs. sequential access. Random is w.r.t.
to blocksize. Defaults to sequential access.
-b bs bs is decimal size of buffer to use, specified as
* N for bytes, Nk for kbytes, Nm for megabytes.
Default is 8k.
-w pct Writes vs. Reads. The argument pct is the
percentage of writes vs. reads.
-f filename name of file to open. Defaults to the name
\"diskex.data\".
-c size For the case where you wish to (re)create
the dat file.
-s size Size of test area within file to exercise.
If not specified, the entire file is used,
modulo block size.
-S sampsize Size of sample interval (default 100)
-h Print this message
Examples:
diskex -f /dev/rsd3c -s 100m -r -b 16k
- execute random reads in the front 100 megabytes of /dev/rsd3c
with a blocksize of 16kbytes.
diskex -f /tmp/bob.data -c 25m -w 25
- create /tmp/bob.data (25 megabytes) and operate on it sequentially
with 25%% of the operations as writes.
Output (if not using the -q flag):
Continuous single line stream of output, e.g.,
read bno 8 50.4428 ops/sec 403.5 kb/sec\n";
int
main(int a, char **v)
{
extern char *optarg;
STAT_T st;
int c, fd;
srand((int)(time((time_t *) 0)/getpid()));
while ((c = getopt(a, v, "hzqrb:w:f:s:c:S:")) != -1) {
switch (c) {
case 'z':
zflag = 1;
break;
case 'q':
verbose = 0;
break;
case 'r':
sequential = 0;
break;
case 'S':
sampsize = (unsigned long) szarg(optarg);
break;
case 'b':
blksize = (unsigned long) szarg(optarg);
if (blksize & (blksize - 1)) {
fprintf(stderr, "%s: blksize must be a power of two\n", *v);
exit(1);
}
break;
case 'w':
wpercent = atoi(optarg);
if (wpercent < 0 || wpercent > 100) {
fprintf(stderr, "%s: bad -w value (%d)\n", *v, wpercent);
exit(1);
}
break;
case 'f':
filename = optarg;
break;
case 's':
fsize = szarg(optarg);
break;
case 'c':
cresize = szarg(optarg);
break;
case 'h':
default:
fprintf(stderr, usage);
exit(1);
}
}
if (optind < a) {
fprintf(stderr, "More options?\n");;
exit(1);
}
buffer = malloc(blksize);
if (buffer == NULL) {
perror("malloc");
exit(1);
}
/*
* Open the file..
*/
if (cresize != (SEEK_T) 0) {
fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY|O_LARGEFILE, 0666);
if (fd < 0) {
perror(filename);
exit(1);
}
while (cresize > 0) {
int amt = cresize > blksize? blksize: cresize;
if (write(fd, buffer, (int) amt) != (int) amt) {
perror(filename);
exit(1);
}
cresize -= (SEEK_T) amt;
}
(void) close(fd);
}
fd = open(filename, O_RDWR, 0666);
if (fd < 0) {
perror(filename);
exit(1);
}
/*
* See if it is a device rather than a 'regular' file...
*/
if (FSTAT(fd, &st) < 0) {
perror("fstat");
exit(1);
}
if (S_ISBLK(st.st_mode)) {
#if defined(__NetBSD__)
char *makeraw(char *);
int rfd, part;
if ((rfd = open(makeraw(filename), O_RDONLY, 0666)) < 0) {
printf("Can't deal with cooked devices\n");
exit(1);
}
if (ioctl(rfd, DIOCGDINFO, (caddr_t) &fred) < 0) {
perror("DIOCGDINFO_raw");
exit(1);
}
seekbase = (wpercent == 0) ? 0 : 8192;
part = filename[strlen(filename) - 1] - 'a';
rfsize = seeklim =
((SEEK_T) fred.d_partitions[part].p_size) * (SEEK_T) DEV_BSIZE;
(void) close(rfd);
#elif defined(sun)
struct dk_allmap x;
char *makeraw(char *);
int rfd, part;
if ((rfd = open(makeraw(filename), O_RDONLY, 0666)) < 0) {
printf("Can't deal with cooked devices\n");
exit(1);
}
#if defined(__svr4__)
part = filename[strlen(filename) - 1] - '0';
#else
part = filename[strlen(filename) - 1] - 'a';
#endif
if (ioctl(rfd, DKIOCGAPART, (caddr_t) &x) < 0) {
perror("DKIOCGAPART_raw");
exit(1);
}
seekbase = (wpercent == 0) ? 0 : 8192;
rfsize = seeklim =
((SEEK_T) x.dka_map[part].dkl_nblk) * (SEEK_T) DEV_BSIZE;
(void) close(rfd);
#elif defined(linux)
if (ioctl(fd, BLKGETSIZE, (caddr_t) &seeklim) < 0) {
perror("BLKGETSIZE");
exit(1);
}
seeklim <<= 9;
rfsize = seeklim;
#else
rfsize = seeklim = (SEEK_T) 1;
#endif
} else if (S_ISCHR(st.st_mode)) {
#if defined(__NetBSD__)
int part;
if (ioctl(fd, DIOCGDINFO, (caddr_t) &fred) < 0) {
perror("DIOCGDINFO");
exit(1);
}
seekbase = (wpercent == 0) ? 0 : 8192;
part = filename[strlen(filename) - 1] - 'a';
rfsize = seeklim =
((SEEK_T) fred.d_partitions[part].p_size) * (SEEK_T) DEV_BSIZE;
#elif defined(sun)
struct dk_allmap x;
int part;
if (blksize < DEV_BSIZE) {
fprintf(stderr, "%s: block size must be at least %d bytes on "
"raw device\n", *v, DEV_BSIZE);
exit(1);
}
#if defined(__svr4__)
part = filename[strlen(filename) - 1] - '0';
#else
part = filename[strlen(filename) - 1] - 'a';
#endif
if (ioctl(fd, DKIOCGAPART, (caddr_t) &x) < 0) {
perror("DKIOCGAPART");
exit(1);
}
seekbase = (wpercent == 0) ? 0 : 8192;
rfsize = seeklim =
((SEEK_T) x.dka_map[part].dkl_nblk) * (SEEK_T) DEV_BSIZE;
#elif defined(convex)
struct topology top;
seeklim = 0;
if (ioctl(fd, SIOC_READ_TOPOLOGY, (caddr_t)&top) >= 0) {
seeklim = (SEEK_T) top.partition[st.st_rdev & 0xf].size *
(SEEK_T) DEV_BSIZE;
}
rfsize = seeklim;
#else
rfsize = seeklim = (SEEK_T) 1;
#endif
} else {
rfsize = seeklim = st.st_size;
}
if (seeklim < (SEEK_T) 0) {
printf("%s too big for lseek(2) call\n", filename);
exit(1);
}
if (fsize) {
if (fsize > seeklim)
fsize = seeklim;
else if (seeklim > fsize)
seeklim = fsize;
} else {
fsize = seeklim;
}
seeklim &= ~(blksize-1);
if (seeklim < seekbase) {
fprintf(stderr, "%s: botch, seeklim (%ld) < seekbase (%ld)\n",
*v, seeklim, seekbase);
exit(1);
}
if (verbose) {
long long fs, rs, sb, sl;
fs = (long long) fsize;
rs = (long long) rfsize;
sb = (long long) seekbase;
sl = (long long) seeklim;
fprintf(stdout, "%s: fsize %lx%08lx (real %lx%08lx) Seek base "
" %lx%08lx Seek lim %lx%08lx\n", *v,
(long) (fs >> 32LL), (long) (fs & 0xFFFFFFFF),
(long) (rs >> 32LL), (long) (rs & 0xFFFFFFFF),
(long) (sb >> 32LL), (long) (sb & 0xFFFFFFFF),
(long) (sl >> 32LL), (long) (sl & 0xFFFFFFFF));
fprintf(stdout, "%s: file %s bs %ld seq %d pct writes: %d\n",
*v, filename, blksize, sequential, wpercent);
}
operate(*v, fd);
exit(0);
}
static SEEK_T
szarg(char *n)
{
register int shift = 0;
register char *q = n;
while (*q != (char) 0)
q++;
q--;
if (*q == 'b' || *q == 'B')
q--;
if (*q == 'k' || *q == 'K') {
shift = 10;
*q = 0;
} else if (*q == 'm' || *q == 'M') {
shift = 20;
*q = 0;
} else if (*q == 'g' || *q == 'G') {
shift = 30;
*q = 0;
}
return ((SEEK_T) strtol((const char *)n, (char **) NULL, 0) << shift);
}
static void
operate(char *pname, int fd)
{
register int i;
register unsigned long offset, etime;
char *optype;
unsigned long *opvec;
struct timeval st, et;
register double opsec, kbsec;
offset = seekbase;
kbsec = opsec = 0;
if (gettimeofday(&st, (struct timezone *) 0) < 0) {
perror("gettimeofday");
exit(1);
/* NOTREACHED */
}
opvec = (unsigned long *) malloc(sampsize * sizeof (unsigned long));
if (opvec == NULL) {
fprintf(stderr, "%s: unable to malloc opvec for sampsize %ld\n",
pname, sampsize);
exit(1);
/* NOTREACHED */
}
optype = (char *) malloc(sampsize * sizeof (char));
if (optype == NULL) {
fprintf(stderr, "%s: unable to malloc optype for sampsize %ld\n",
pname, sampsize);
exit(1);
/* NOTREACHED */
}
#if defined(sun) && defined(__svr4__)
setbuf(stdout, NULL);
#endif
for (;;) {
for (i = 0; i < sampsize; i++) {
if (sequential) {
if (offset + blksize > seeklim)
offset = seekbase;
opvec[i] = offset;
offset += blksize;
} else {
do {
opvec[i] = rand() % seeklim;
} while (opvec[i] < seekbase || opvec[i] >= seeklim - blksize);
}
if ((float) i < (((float) wpercent / 100.0) * (float) sampsize))
optype[i] = 1;
else
optype[i] = 0;
}
for (i = 0; i < sampsize; i++) {
if (SEEK(fd, (off_t) opvec[i], 0) < 0) {
fprintf(stderr, "%s: SEEK fails, errno %d\n", pname, errno);
return;
}
if (optype[i]) {
if (write(fd, buffer, blksize) != blksize) {
perror("\nwrite");
return;
}
} else {
if (read(fd, buffer, blksize) != blksize) {
perror("\nread");
return;
}
}
if (verbose) {
printf("%6s bno %6ld %10.4f ops/sec %8.1f kb/sec\r",
optype[i]? "write" : "read", opvec[i] / blksize,
opsec, kbsec);
}
}
if (gettimeofday(&et, (struct timezone *) 0) < 0) {
perror("\ngettimeofday");
exit(1);
}
etime = et.tv_sec - st.tv_sec;
if (et.tv_usec < st.tv_usec) {
etime--;
etime *= 1000000;
etime += (et.tv_usec + 1000000 - st.tv_usec);
} else {
etime *= 1000000;
etime += (et.tv_usec - st.tv_usec);
}
opsec = (double) etime / (double) 1000000.0;
kbsec = ((blksize * (double) sampsize) / opsec) / 1024.0;
opsec = (double) sampsize / (double) opsec;
st = et;
#if defined(linux)
if (zflag)
(void) ioctl(fd, BLKFLSBUF, 0);
#endif
}
}
#if defined(sun) || defined(convex) || defined(__NetBSD__)
#include <memory.h>
char *
makeraw(char *fn)
{
register char *ns, *p;
ns = malloc(strlen(fn) + 2);
memset(ns, 0, strlen(fn) + 2);
#if defined(sun) && defined(__svr4__)
if (strncmp(fn, "/dev/dsk", 8) == 0) {
strcpy(ns, "/dev/rdsk");
strcat(ns, fn+8);
return (ns);
} else if (strncmp(fn, "dsk", 3) == 0) {
strcpy(ns, "rdsk");
strcat(ns, fn+3);
return (ns);
}
#endif
p = strrchr(fn, '/');
if (p == (char *) 0) {
ns[0] = 'r';
(void) strcpy(&ns[1], fn);
} else {
p++;
(void) strncpy(ns, fn, p - fn);
(void) strcat(ns, "r");
(void) strcat(ns, p);
}
return (ns);
}
#endif
/*
* ---------------------------------------------------------------------------
* Local variables:
* c-indent-level: 4
* c-brace-imaginary-offset: 0
* c-brace-offset: -4
* c-argdecl-indent: 4
* c-label-offset: -4
* c-continued-statement-offset: 4
* c-continued-brace-offset: 0
* End:
*/