/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __osf__ #include #endif #ifdef BSD4_4 #include #if __FreeBSD__ >= 5 #include #endif #endif #include "randoms.h" /* submitted by Andy Doran */ #ifdef __NetBSD__ #include #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 \tEnsure that all buffers are aligned on an 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 \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 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); }