/* rawio.c: measure raw disk I/O throughput */
/*-
* Copyright (c) 1999
* Nan Yang Computer Services Limited. All rights reserved.
*
* This software is distributed under the so-called ``Berkeley
* License'':
*
* 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 Nan Yang Computer
* Services Limited.
* 4. Neither the name of the Company nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* This software is provided ``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 company or contributors 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.
*
* $Id: rawio.c,v 1.7 1999/07/21 02:10:09 grog Exp grog $
*/
#define LINELENGTH 200 /* max length of program line */
#define MAXLINES 54 /* number of lines on page */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <signal.h>
#ifdef __osf__
#include <sys/ioctl.h>
#endif
#ifdef BSD4_4
#include <sys/disklabel.h>
#if __FreeBSD__ >= 5
#include <sys/disk.h>
#endif
#endif
#include "randoms.h"
/* submitted by Andy Doran <ad@psn.ie> */
#ifdef __NetBSD__
#include <errno.h>
#endif
#if defined(__NetBSD__) || defined(__osf__) || defined(linux)
#define srandomdev() srand ((unsigned) time (NULL))
#endif
#ifdef __alpha
#define Int64 long
#define Quad "l"
#define atoi64(x) strtol(x, NULL, 10)
#else
#define Int64 long long
#define Quad "ll"
#define atoi64(x) strtoq(x, NULL, 10)
#endif
#define SKIPSTART 16384 /* amount to leave untouched at start of dev */
#ifdef __osf__
pid_t wait4 (pid_t process_id, int *status_location, int options, struct rusage *resource_usage);
#endif
void checklines (int increment);
void usage (void);
void sigcatch (int signo);
int myrandom (int i);
int myrandom2 (int i);
#ifndef MAXPHYS
#define MAXPHYS (128*1024)
#endif
char physbuf [MAXPHYS * 2]; /* buffer we're allocating from */
char *buf; /* and what we're using, for alignment */
int file;
size_t length;
u_int sectorsize;
int count;
enum operation
{
RandomRead = 1,
SequentialRead = 2,
RandomWrite = 4,
SequentialWrite = 8
}
op;
enum
{
RRtest = 0,
SRtest,
RWtest,
SWtest,
};
int openflags; /* flags to pass to open(2) */
enum operation thisop; /* current one we're looking at */
int test; /* number of test */
char *args;
char *myname; /* argv [0] for posterity */
off_t offset;
int lines;
#define MAXCHILD 256 /* maximum number of processes to start */
pid_t apid [MAXCHILD];
int nproc = 8; /* (default) number to start */
int fixedoffset = 0; /* set if we start all sequential tests
* at the same offset (-f flag) */
int fixedlength = 0; /* set if random transfers are all full
* length (-F flag) */
int staticrandom = 0; /* set if we read our random values from
* the internal table (-S flag) */
struct timeval start_time;
struct timeval end_time [MAXCHILD]; /* one end time per process */
float elapsed_time;
float user_time;
float system_time;
struct rusage rusage [MAXCHILD]; /* and associated rusage info */
struct rusage total_rusage; /* total values */
pid_t endproc [MAXCHILD]; /* note the pid of each proc as it finishes */
/* Shared memory segment for communicating with
* the children. Each child accesses only its
* element of the array. The parent doesn't
* touch them until they exit */
struct childinfo
{
int reads;
int writes;
Int64 bytes_read;
Int64 bytes_written;
}
*childinfo;
int total_reads;
int total_writes;
Int64 total_bytes_read;
Int64 total_bytes_written;
void header ();
/* child code */
void dochild (int, int);
#define PAGELENGTH 60 /* get this from termcap */
void
checklines (int increment)
{
char prompt [2];
if ((lines += increment) >= PAGELENGTH)
{
fprintf (stderr, "More...");
fgets (prompt, 2, stdin);
lines = 0;
}
}
int alignment = 1; /* buffer alignment */
size_t maxchunk = (size_t) 16384; /* maximum size of transfer */
off_t filesize = 0;
int recs = 16384; /* number of records to transfer */
char *device; /* name of device to open */
int proc; /* index of this process in childinfo (child) */
/* index of current child (parent) */
int verbose = 0; /* verbosity flag */
int heading = 1; /* print heading if != 0 */
int exitimmediately = 0; /* exit after headers if set (-x flag) */
int SWfrac; /* percentage sequential writes */
int RWfrac; /* percentage random writes */
char *id; /* name of test */
void
usage ()
{
fprintf (stderr,
"Usage: %s [options] device\n"
"\n"
"Options may be:\n"
"\n"
"\t-a\tPerform all tests (read and write, random and sequential)\n"
"\t-A <alignment>\tEnsure that all buffers are aligned on an <alignment> boundary\n"
"\t-c\tMaximum I/O transfer size in sectors or bytes.\n"
"\t\t\tMust be an integral number of sectors, default %d bytes\n"
"\t-F\tPerform fixed-length random I/O\n"
"\t-f\tStart sequential tests at fixed offset\n"
"\t-h\tSuppress heading\n"
"\t-I <name>\tIdentification for output\n"
"\t-n\tTotal number of records to write, defaults to %d\n"
"\t-p\tNumber of processes to start, defaults to %d\n"
"\t-R\tPerform random read test\n"
"\t-r\tPerform sequential read test\n"
"\t-s\tSize of file system, no default\n"
"\t-S\tUse static random table\n"
"\t-v\tProduce verbose output\n"
"\t-W\tPerform random write test\n"
"\t-Wx\tPerform random read/write test, x%% write\n"
"\t-w\tPerform sequential write test\n"
"\t-wx\tPerform sequential read/write test, x%% write\n"
"\t-x\tPrint headers and exit\n"
"\n",
myname,
maxchunk,
recs,
nproc);
exit (1);
}
/*
* Take a number with an optional scale factor and convert
* it to a number of bytes.
*
* The scale factors are:
*
* s sectors (of 512 bytes)
* k kilobytes (1024 bytes)
* m megabytes (of 1024 * 1024 bytes)
* g gigabytes (of 1024 * 1024 * 1024 bytes)
*/
u_int64_t sizespec (char *spec)
{
u_int64_t size;
char *s;
int sign = 1; /* -1 if negative */
size = 0;
if (spec != NULL) /* we have a parameter */
{
s = spec;
if (*s == '-') /* negative, */
{
sign = -1;
s++; /* skip */
}
if ((*s >= '0') && (*s <= '9')) /* it's numeric */
{
while ((*s >= '0') && (*s <= '9')) /* it's numeric */
size = size * 10 + *s++ - '0'; /* convert it */
switch (*s)
{
case '\0':
return size * sign;
case 'S':
case 's':
return size * sign * 512;
case 'K':
case 'k':
return size * sign * 1024;
case 'M':
case 'm':
return size * sign * 1024 * 1024;
case 'G':
case 'g':
return size * sign * 1024 * 1024 * 1024;
}
}
}
fprintf (stderr, "Invalid length specification: %s", spec);
exit (1);
}
int main (int argc, unsigned char *argv [])
{
int i;
char *arg;
myname = argv [0]; /* for usage () */
if (argc < 2)
usage ();
for (i = 1; i < argc; i++)
{
if (argv [i][0] == '-')
{
switch (argv [i][1])
{
case 'a': /* do it all */
op = SequentialRead | RandomRead | SequentialWrite | RandomWrite;
break;
case 'A':
if (argv [i] [2] != '\0')
arg = &argv [i] [2];
else if (++i < argc)
arg = argv [i];
else
{
printf ("No arg to A flag\n");
break;
}
alignment = atoi (arg);
break;
case 'c': /* maxchunk */
if (argv [i] [2] != '\0')
arg = &argv [i] [2];
else if (++i < argc)
arg = argv [i];
else
{
printf ("No arg to c flag\n");
break;
}
maxchunk = (size_t) atoi (arg);
if (maxchunk < (size_t) 512)
{
maxchunk *= (size_t) 512;
}
if (maxchunk > (size_t) MAXPHYS)
{
printf ("Maximum transfer size limited to %d\n", MAXPHYS);
maxchunk = (size_t) MAXPHYS;
}
break;
case 'f':
fixedoffset = 1;
break;
case 'F':
fixedlength = 1;
break;
case 'h':
heading = 0;
break;
case 'I':
if (argv [i] [2] != '\0')
arg = &argv [i] [2];
else if (++i < argc)
arg = argv [i];
else
{
printf ("No arg to I flag\n");
break;
}
id = arg;
break;
case 'n':
if (argv [i] [2] != '\0')
arg = &argv [i] [2];
else if (++i < argc)
arg = argv [i];
else
{
printf ("No arg to n flag\n");
break;
}
recs = atoi (arg);
break;
case 'p':
if (argv [i] [2] != '\0')
arg = &argv [i] [2];
else if (++i < argc)
arg = argv [i];
else
{
printf ("No arg to n flag\n");
break;
}
nproc = atoi (arg);
if (nproc > MAXCHILD) /* too many */
{
fprintf (stderr,
"Can't start %d processes, maximum is %d\n",
nproc,
MAXCHILD );
nproc = MAXCHILD;
}
break;
case 'R':
op |= RandomRead;
break;
case 'r':
op |= SequentialRead;
break;
case 's':
if (argv [i] [2] != '\0')
arg = &argv [i] [2];
else if (++i < argc)
arg = argv [i];
else
{
printf ("No arg to n flag\n");
break;
}
filesize = sizespec (arg);
break;
case 'S':
staticrandom = 1; /* read from our internal table */
break;
case 'v':
if (argv [i] [2] != '\0')
arg = &argv [i] [2];
else if (++i < argc)
arg = argv [i];
else
{
printf ("No arg to v flag\n");
break;
}
verbose = atoi (arg);
break;
case 'W':
op |= RandomWrite;
if (argv [i] [2] != '\0')
RWfrac = atoi (&argv [i] [2]);
else if (((i + 1) < argc) && isdigit (argv [i + 1] [0]))
{
RWfrac = atoi (argv [i + 1]);
i++;
}
else
RWfrac = 100;
if ((RWfrac < 0) || (RWfrac > 100))
{
fprintf (stderr, "Invalid random write percentage specification\n");
exit (1);
}
break;
case 'w':
op |= SequentialWrite;
if (argv [i] [2] != '\0')
SWfrac = atoi (&argv [i] [2]);
else if (((i + 1) < argc) && isdigit (argv [i + 1] [0]))
{
SWfrac = atoi (argv [i + 1]);
i++;
}
else
SWfrac = 100;
if ((SWfrac <= 0) || (SWfrac > 100))
{
fprintf (stderr, "Invalid sequential write percentage specification\n");
exit (1);
}
break;
case 'x':
exitimmediately = 1;
break;
default:
usage ();
}
}
else if (device != NULL)
usage ();
else
device = argv [i];
}
if (exitimmediately)
{
header ();
exit (0);
}
buf = (char *) (((int) &physbuf [MAXPHYS]) & ~ (alignment - 1)); /* where to put the aligned buffer */
if (op == 0) /* no ops specified, */
op = RandomRead | SequentialRead; /* default to the read tests */
if (device == NULL)
{
fprintf (stderr, "No file name specified\n");
usage ();
}
if (op & ((SequentialWrite | RandomWrite) == 0)) /* only read tests */
openflags = O_RDONLY; /* open read-only */
else
openflags = O_RDWR; /* open read-write */
if ((file = open (device, openflags, 0)) < 0)
{
fprintf (stderr, "Can't open %s: %s\n", device, strerror (errno));
usage ();
}
if (filesize == 0)
{
struct stat filestat;
if (stat (device, &filestat) < 0)
{
fprintf (stderr, "Can't stat %s: %s\n", device, strerror (errno));
usage ();
}
else
filesize = filestat.st_size;
if ((filestat.st_mode & S_IFMT) == S_IFBLK)
fprintf (stderr,
"Warning: %s is a block device. Results will be meaningless\n",
device);
else if ((filestat.st_mode & S_IFMT) != S_IFCHR)
{
fprintf (stderr, "%s is not a device\n", device);
usage ();
}
/* stat didn't do it; try lseek */
if (filesize == 0)
{
filesize = lseek (file, (off_t) 0, SEEK_END); /* try to get the end */
if (filesize < 0)
fprintf (stderr, "Can't seek %s: %s\n", device, strerror (errno));
}
#ifdef BSD4_4
/* lseek didn't do it; look at the disk label */
if (filesize == 0)
{
struct disklabel label;
if (ioctl (file, DIOCGDINFO, &label) != -1) /* got the label */
filesize = ((off_t) label.d_secperunit) * DEV_BSIZE;
else if (verbose)
fprintf (stderr, "Can't get label of %s: %s\n", device, strerror (errno));
}
#endif
}
if (filesize == 0)
{
fprintf (stderr, "No file size specified\n");
usage ();
}
#ifndef DIOCGSECTORSIZE
sectorsize = DEV_BSIZE;
#else
if (ioctl (file, DIOCGSECTORSIZE, §orsize) == -1) /* failed to find native sector size */
{
if (verbose)
fprintf (stderr, "Can't get sector size of %s: %s\n", device, strerror (errno));
sectorsize = DEV_BSIZE;
}
#endif
if (maxchunk <= sectorsize)
{
fprintf (stderr, "I/O transfer max must be at least 1 sector\n");
usage ();
}
if (maxchunk & (sectorsize - 1))
{
fprintf (stderr, "Invalid transfer size, must be multiple of %d: %d\n", sectorsize, maxchunk);
usage ();
}
if (id == NULL) /* no ID specified, */
{
id = strrchr (device, '/'); /* find the basename */
if (id == NULL) /* no / */
id = device; /* take the whole name */
else
id++;
if ((id [0] == 'r') && (id [1] != '\0')) /* raw name? */
id++;
}
#ifdef BSD4_4
childinfo = mmap (NULL,
nproc * sizeof (struct childinfo),
PROT_READ | PROT_WRITE,
#ifdef MAP_INHERIT
MAP_INHERIT | MAP_SHARED | MAP_ANON,
#else
MAP_SHARED | MAP_ANON,
#endif
-1,
(off_t) 0 );
#else
childinfo = mmap (NULL,
nproc * sizeof (struct childinfo),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANON,
-1,
(off_t) 0 );
#endif
if (childinfo == (struct childinfo *) MAP_FAILED)
{
fprintf (stderr, "Can't mmap shared memory: %s\n", strerror (errno));
exit (1);
}
/* OK, we're ready to go */
header ();
if (! verbose) /* print the ID now */
printf ("%-8s ", id);
for (test = 0; test < 4; test++) /* check for each test */
{
if (op & 1 << test) /* do this test */
{
fflush (stdout); /* to stop the children doing it */
/* First, start up the procs */
for (proc = 0; proc < nproc; proc++) /* spawn some processes */
{
pid_t pid = fork ();
if (pid == 0)
dochild (test, proc);
else if (pid < 0)
{
fprintf (stderr, "Can't fork process %d: %s\n", proc, strerror (errno));
exit (1);
}
else
apid [proc] = pid;
}
sleep (1); /* give them a sec to get their act together */
/* Now initialize the totals */
bzero ((char *) &total_rusage, sizeof (struct rusage));
total_reads = 0;
total_writes = 0;
total_bytes_read = 0;
total_bytes_written = 0;
/* Now we're ready to start: note the time */
gettimeofday (&start_time, NULL);
/* And let them go */
for (proc = 0; proc < nproc; proc++)
{
if (kill (apid [proc], SIGUSR1) < 0)
fprintf (stderr, "Can't kill process %d: %s\n", apid [proc], strerror (errno));
}
/* Now wait for them to finish */
for (proc = 0; proc < nproc; proc++)
{
int status;
pid_t pid;
pid = wait4 (-1, &status, 0, &rusage [proc]);
gettimeofday (&end_time [proc], NULL);
if (pid < 0)
{
fprintf (stderr, "Can't wait4: %s\n", strerror (errno));
exit (1);
}
endproc [proc] = pid; /* note the pid */
}
for (proc = 0; proc < nproc; proc++)
{
total_rusage.ru_utime.tv_sec += rusage [proc].ru_utime.tv_sec; /* accumulate totals */
total_rusage.ru_utime.tv_usec += rusage [proc].ru_utime.tv_usec; /* accumulate totals */
total_rusage.ru_stime.tv_sec += rusage [proc].ru_stime.tv_sec; /* accumulate totals */
total_rusage.ru_stime.tv_usec += rusage [proc].ru_stime.tv_usec; /* accumulate totals */
total_rusage.ru_minflt += rusage [proc].ru_minflt; /* page reclaims */
total_rusage.ru_majflt += rusage [proc].ru_majflt; /* page faults */
total_rusage.ru_nswap += rusage [proc].ru_nswap; /* swaps */
total_rusage.ru_inblock += rusage [proc].ru_inblock; /* block input operations */
total_rusage.ru_oublock += rusage [proc].ru_oublock; /* block output operations */
total_rusage.ru_msgsnd += rusage [proc].ru_msgsnd; /* messages sent */
total_rusage.ru_msgrcv += rusage [proc].ru_msgrcv; /* messages received */
total_rusage.ru_nsignals += rusage [proc].ru_nsignals; /* signals received */
total_rusage.ru_nvcsw += rusage [proc].ru_nvcsw; /* voluntary context switches */
total_rusage.ru_nivcsw += rusage [proc].ru_nivcsw; /* involuntary " */
total_reads += childinfo [proc].reads;
total_writes += childinfo [proc].writes;
total_bytes_read += childinfo [proc].bytes_read;
total_bytes_written += childinfo [proc].bytes_written;
}
/* Reaped all children. Print results */
proc = nproc - 1; /* look at the last child to complete */
elapsed_time = (end_time [proc].tv_sec - start_time.tv_sec)
+ ((float) (end_time [proc].tv_usec - start_time.tv_usec) / 1000000);
user_time = total_rusage.ru_utime.tv_sec + ((float) total_rusage.ru_utime.tv_usec) / 1000000;
system_time = total_rusage.ru_stime.tv_sec + ((float) total_rusage.ru_stime.tv_usec) / 1000000;
if (verbose)
{
/* First print the test names */
switch (test)
{
case RRtest:
printf ("RR ");
break;
case SRtest:
printf ("SR ");
break;
case RWtest:
if (RWfrac != 100)
printf ("RW%02d", RWfrac);
else
printf ("RW ");
break;
case SWtest:
if (SWfrac != 100)
printf ("SW%02d", SWfrac);
else
printf ("SW ");
break;
}
if (verbose > 1)
printf ("%8s\t%12.6f\t%10.1f\t %5.0f %6.1f\t%6.1f\t%6.1f\t%d\t%d\n",
id,
elapsed_time,
(float) (total_bytes_read + total_bytes_written) / elapsed_time / 1000,
((float) (total_reads + total_writes) / elapsed_time),
user_time / elapsed_time * 100,
system_time / elapsed_time * 100,
(user_time + system_time) / elapsed_time * 100,
total_reads,
total_writes );
else
{
if (test < 2) /* read ops, */
printf ("%8s\t%10.1f\t %5.0f %6.1f\t%6.1f\t%6.1f\t%d\n",
id,
(float) (total_bytes_read + total_bytes_written) / elapsed_time / 1000,
((float) (total_reads + total_writes) / elapsed_time),
user_time / elapsed_time * 100,
system_time / elapsed_time * 100,
(user_time + system_time) / elapsed_time * 100,
total_reads );
else
printf ("%8s\t%10.1f\t %5.0f %6.1f\t%6.1f\t%6.1f\t%d\n",
id,
(float) (total_bytes_read + total_bytes_written) / elapsed_time / 1000,
((float) (total_reads + total_writes) / elapsed_time),
user_time / elapsed_time * 100,
system_time / elapsed_time * 100,
(user_time + system_time) / elapsed_time * 100,
total_writes );
}
}
else /* not verbose, */
printf ("%8.1f %5.0f\t",
(float) (total_bytes_read + total_bytes_written) / elapsed_time / 1000,
((float) (total_reads + total_writes) / elapsed_time) );
}
else if (! verbose)
printf ("\t\t"); /* skip the column */
}
if (! verbose)
printf ("\n");
return 0;
}
void header ()
{
if (heading)
{
if (verbose > 1)
{
printf ("Test name:\t%16s\n"
"Transfer size:\t%16d\n"
"Record count:\t\t%8d\n"
"Process count:\t%16d\n"
"Device size:\t%16" Quad "d\n",
id,
maxchunk,
recs,
nproc,
filesize );
/* printf ("Test ID\t\tTime\t KB/sec\t %%User\t %%Sys\t%%Total\tReads\tI/O in\tWrites\tI/O out\n"); */
printf ("Test ID\t\tTime\t KB/sec\t /sec %%User\t %%Sys\t%%Total\tReads\tWrites\n");
}
else if (verbose)
printf ("Test ID\t K/sec\t /sec %%User\t %%Sys\t%%Total\n");
else
printf ("\t Random read\tSequential read\t Random write\tSequential write\n"
"ID\t K/sec /sec\t K/sec /sec\t K/sec /sec\t K/sec /sec\n");
}
}
void sigcatch (int signo)
{
}
/*
* Calculate a random number. If we're static, just pick it from a
* table, otherwise do the real thing.
*/
int myrandom (int i)
{
if (staticrandom)
return randoms [i % MAXRANDOM];
else
return random ();
}
/* Another random number calculation. Do we really need this? */
int myrandom2 (int i)
{
if (staticrandom)
return random2 [i % MAXRANDOM];
else
return random ();
}
/* Child code */
void dochild (int test, int proc)
{
sigset_t allsigs;
struct sigaction ignore;
int mycount;
int iocount;
int i;
int maxrecs;
off_t pos; /* where we actually seek to */
ignore.sa_handler = sigcatch;
ignore.sa_flags = 0;
ignore.sa_mask = allsigs;
/* first, calculate the number of records to transfer.
* If we're one of the first <remainder> processes,
* transfer one more */
mycount = recs / nproc; /* quotient */
if (recs % nproc > proc)
mycount++;
/* Initialize the counters */
childinfo [proc].reads = 0;
childinfo [proc].bytes_read = 0;
childinfo [proc].writes = 0;
childinfo [proc].bytes_written = 0;
/* Don't jump the gun */
sigemptyset (&allsigs);
if (sigaction (SIGUSR1, &ignore, NULL) < 0)
{
perror ("Can't sigaction");
exit (1);
}
sigsuspend (&allsigs);
srandomdev ();
/* OK, we're allowed to start now */
switch (test)
{
case 0: /* random read */
for (i = 1; i <= mycount; i++)
{
if (fixedlength)
length = maxchunk;
else
length = (myrandom (proc + nproc * i * 2)
% (maxchunk * 2) + sectorsize) & ~(sectorsize - 1); /* length of this transfer */
offset = (((((off_t) myrandom2 (proc + nproc * i)) * sectorsize)
% (filesize - SKIPSTART - length)) & ~((off_t)sectorsize - 1)) + SKIPSTART;
if ((iocount = pread (file, buf, length, pos = offset)) != length)
{
int Errno = errno;
pos = offset + (iocount > 0 ? iocount : 0);
fprintf (stderr,
"offset %" Quad "d, filesize %" Quad "d\n",
offset,
filesize);
if (iocount < 0) /* error */
fprintf (stderr,
"Child %d Bad read at %" Quad "d: %s (%d), iocount %d\n",
proc,
pos,
strerror (Errno),
Errno,
iocount );
else
fprintf (stderr,
"Child %d Bad read at %" Quad "d: %d of %d bytes\n",
proc,
pos,
iocount,
length );
exit (1);
}
else
{
childinfo [proc].reads++;
childinfo [proc].bytes_read += iocount;
}
}
break;
case 1: /* Sequential Read */
maxrecs = mycount;
length = maxchunk; /* length of this transfer */
if (length * maxrecs > (filesize - SKIPSTART)) /* too much, */
maxrecs = (filesize - SKIPSTART) / length; /* reduce */
if (fixedoffset)
offset = SKIPSTART; /* start at the beginning */
else /* random start */
offset = (((((off_t) myrandom (proc + nproc)) * sectorsize)
% (filesize - SKIPSTART /* decide where to start */
- (maxrecs * length))) & ~((off_t)sectorsize - 1)) + SKIPSTART;
if ((offset + maxrecs * length) > filesize) /* XXX */
{
printf ("Overrun: offset %" Quad "d, end %" Quad "d, file size %" Quad "d\n",
offset,
offset + maxrecs * length,
filesize);
exit (1);
}
if ((verbose > 2) && ! fixedoffset)
printf ("Child %d reading from %" Quad "d\n", proc, offset);
for (i = 1; i <= maxrecs; i++)
{
if ((iocount = pread (file, buf, length, offset)) != length)
{
int Errno = errno;
off_t pos = offset + (iocount > 0 ? iocount : 0);
fprintf (stderr,
"offset %" Quad "d, filesize %" Quad "d\n",
offset,
filesize);
if (iocount < 0) /* error */
fprintf (stderr,
"Child %d Bad read at %" Quad "d: %s (%d)\n",
proc,
pos,
strerror (Errno),
Errno );
else
fprintf (stderr,
"Child %d Bad read at %" Quad "d: %d of %d bytes\n",
proc,
pos,
iocount,
length );
exit (1);
}
else
{
childinfo [proc].reads++;
childinfo [proc].bytes_read += iocount;
offset += iocount;
}
}
break;
case 2: /* Random Write */
for (i = 1; i <= mycount; i++)
{
if (fixedlength)
length = maxchunk;
else
length = (myrandom (proc + nproc * i * 2)
% (maxchunk * 2) + sectorsize) & ~(sectorsize - 1); /* length of this transfer */
offset = (((((off_t) myrandom2 (proc + nproc * i)) * sectorsize)
% (filesize - SKIPSTART - length)) & ~((off_t)sectorsize - 1)) + SKIPSTART;
if (1)
{
if ((RWfrac == 100)
|| ((myrandom (proc + nproc * i * 2 + 1) % 100) < RWfrac) )
{
iocount = pwrite (file, buf, length, offset);
childinfo [proc].writes++;
childinfo [proc].bytes_written += iocount;
}
else
{
iocount = pread (file, buf, length, offset);
childinfo [proc].reads++;
childinfo [proc].bytes_read += iocount;
}
if (iocount != length)
{
int Errno = errno;
off_t pos = offset + (iocount > 0 ? iocount : 0);
fprintf (stderr,
"offset %" Quad "d, filesize %" Quad "d\n", offset,
filesize);
if (iocount < 0) /* error */
fprintf (stderr,
"Child %d Bad write at %" Quad "d: %s (%d)\n",
proc,
pos,
strerror (Errno),
Errno );
else
fprintf (stderr,
"Child %d Bad write at %" Quad "d: %d of %d bytes\n",
proc,
pos,
iocount,
length );
exit (1);
}
}
}
break;
case 3: /* Sequential Write */
maxrecs = mycount;
length = maxchunk; /* length of this transfer */
if (length * maxrecs > (filesize - SKIPSTART)) /* too much, */
maxrecs = (filesize - SKIPSTART) / length; /* reduce */
if (fixedoffset)
offset = SKIPSTART; /* start at the beginning */
else /* random start */
offset = (((((off_t) myrandom (proc + nproc)) * sectorsize)
% (filesize - SKIPSTART /* decide where to start */
- (maxrecs * length))) & ~((off_t)sectorsize - 1)) + SKIPSTART;
if ((verbose > 2) && ! fixedoffset)
printf ("Child %d writing to %" Quad "d\n", proc, offset);
for (i = 1; i <= maxrecs; i++)
{
if ((SWfrac == 100)
|| ((myrandom (proc + nproc * i * 2 + 1) % 100) < SWfrac) )
{
iocount = pwrite (file, buf, length, offset);
childinfo [proc].writes++;
childinfo [proc].bytes_written += iocount;
}
else
{
iocount = pread (file, buf, length, offset);
childinfo [proc].reads++;
childinfo [proc].bytes_read += iocount;
}
if (iocount != length)
{
int Errno = errno;
off_t pos = offset + (iocount > 0 ? iocount : 0);
fprintf (stderr,
"offset %" Quad "d, filesize %" Quad "d\n",
offset,
filesize);
if (iocount < 0) /* error */
fprintf (stderr,
"Child %d Bad write at %" Quad "d: %s (%d)\n",
proc,
pos,
strerror (Errno),
Errno );
else
fprintf (stderr,
"Child %d Bad write at %" Quad "d: %d of %d bytes\n",
proc,
pos,
iocount,
length );
exit (1);
}
else
{
offset += iocount;
}
}
break;
}
if (verbose > 3) /* really just debug */
fprintf (stderr,
"Child %d: %8d %8lld %8d %8" Quad "d\n",
proc,
childinfo [proc].reads,
childinfo [proc].bytes_read,
childinfo [proc].writes,
childinfo [proc].bytes_written );
exit (0);
}
syntax highlighted by Code2HTML, v. 0.9.1