/* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License along * with this program; if not, write the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ */ /* * iogen - a tool for generating file/sds io for a doio process */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <signal.h> #include <time.h> #include <sys/param.h> #include <sys/types.h> #include <sys/time.h> #include <sys/stat.h> #include <sys/sysmacros.h> #ifdef CRAY #include <sys/file.h> #include <sys/iosw.h> #include <sys/listio.h> #endif #ifdef sgi #include <sys/statvfs.h> #include <sys/fs/xfs_itable.h> #endif #ifdef CRAY #include "libkern.h" #endif #include "doio.h" #include "bytes_by_prefix.h" #include "string_to_tokens.h" #include "open_flags.h" #include "random_range.h" #ifndef PATH_MAX #define PATH_MAX 512 /* ??? */ #endif #ifndef BSIZE #ifdef linux #define BSIZE DEV_BSIZE #else #define BSIZE 512 #endif #endif #define RAW_IO(_flags_) ((_flags_) & (O_RAW | O_SSD)) #ifndef __linux__ extern char *sys_errlist[]; #endif #define SYSERR strerror(errno) /* * Structure for retaining test file information */ struct file_info { char f_path[MAX_FNAME_LENGTH + 1]; /* file name (full path) */ int f_length; /* length in bytes */ int f_iou; /* file iounit */ int f_riou; /* file raw iounit (for O_RAW/O_SSD) */ int f_dalign; /* direct I/O alignment */ int f_nextoff; /* offset of end of last io operation */ int f_type; /* file type S_IFREG, etc... */ int f_lastoffset; /* offset of last io operation */ int f_lastlength; /* length of last io operation */ }; /* * Simple structure for associating strings with values - useful for converting * cmdline args to internal values, as well as printing internal values in * a human readable form. */ struct strmap { char *m_string; int m_value; int m_flags; }; void startup_info(FILE * stream, int seed); int init_output(void); int form_iorequest(struct io_req *req); int get_file_info(struct file_info *rec); int create_file(char *path, int nbytes); int str_to_value(struct strmap *map, char *str); struct strmap *str_lookup(struct strmap *map, char *str); char *value_to_string(struct strmap *map, int val); int parse_cmdline(int argc, char **argv, char *opts); int help(FILE * stream); int usage(FILE * stream); /* * Declare cmdline option flags/variables initialized in parse_cmdline() */ #define OPTS "a:dhf:i:L:m:op:qr:s:t:T:O:N:" int a_opt = 0; /* async io comp. types supplied */ int o_opt = 0; /* form overlapping requests */ int f_opt = 0; /* test flags */ int i_opt = 0; /* iterations - 0 implies infinite */ int L_opt = 0; /* listio min-max nstrides & nents */ int m_opt = 0; /* offset mode */ int O_opt = 0; /* file creation Open flags */ int p_opt = 0; /* output pipe - default is stdout */ int r_opt = 0; /* specify raw io multiple instead of */ /* getting it from the mounted on device. */ /* Only applies to regular files. */ int s_opt = 0; /* syscalls */ int t_opt = 0; /* min transfer size (bytes) */ int T_opt = 0; /* max transfer size (bytes) */ int q_opt = 0; /* quiet operation on startup */ char TagName[40]; /* name of this iogen (see Monster) */ struct strmap *Offset_Mode; /* M_SEQUENTIAL, M_RANDOM, etc. */ int Iterations; /* # requests to generate (0 --> infinite) */ int Time_Mode = 0; /* non-zero if Iterations is in seconds */ /* (ie. -i arg was suffixed with 's') */ char *Outpipe; /* Pipe to write output to if p_opt */ int Mintrans; /* min io transfer size */ int Maxtrans; /* max io transfer size */ int Rawmult; /* raw/ssd io multiple (from -r) */ int Minstrides; /* min # of listio strides per request */ int Maxstrides; /* max # of listio strides per request */ int Oflags; /* open(2) flags for creating files */ int Ocbits; /* open(2) cbits for creating files */ int Ocblks; /* open(2) cblks for creating files */ int Orealtime = 0; /* flag set for -O REALTIME */ int Oextsize = 0; /* real-time extent size */ int Oreserve = 1; /* flag for -O [no]reserve */ int Oallocate = 0; /* flag for -O allocate */ int Owrite = 1; /* flag for -O nowrite */ int Nfiles = 0; /* # files on cmdline */ struct file_info *File_List; /* info about each file */ int Nflags = 0; /* # flags on cmdline */ struct strmap *Flag_List[128]; /* flags selected from cmdline */ int Nsyscalls = 0; /* # syscalls on cmdline */ struct strmap *Syscall_List[128]; /* syscalls selected on cmdline */ int Fileio = 0; /* flag indicating that a file */ /* io syscall has been chosen. */ int Naio_Strat_Types = 0; /* # async io completion types */ struct strmap *Aio_Strat_List[128]; /* Async io completion types */ /* * Map async io completion modes (-a args) names to values. Macros are * defined in doio.h. */ struct strmap Aio_Strat_Map[] = { #ifndef linux {"poll", A_POLL}, {"signal", A_SIGNAL}, #else {"none", 0}, #endif /* !linux */ #ifdef CRAY #if _UMK || RELEASE_LEVEL >= 8000 {"recall", A_RECALL}, #endif #ifdef RECALL_SIZEOF {"recalla", A_RECALLA}, #endif {"recalls", A_RECALLS}, #endif /* CRAY */ #ifdef sgi {"suspend", A_SUSPEND}, {"callback", A_CALLBACK}, #endif {NULL, -1} }; /* * Offset_Mode #defines */ #define M_RANDOM 1 #define M_SEQUENTIAL 2 #define M_REVERSE 3 /* * Map offset mode (-m args) names to values */ struct strmap Omode_Map[] = { {"random", M_RANDOM}, {"sequential", M_SEQUENTIAL}, {"reverse", M_REVERSE}, {NULL, -1} }; /* * Map syscall names (-s args) to values - macros are defined in doio.h. */ #define SY_ASYNC 00001 #define SY_WRITE 00002 #define SY_SDS 00010 #define SY_LISTIO 00020 #define SY_NENT 00100 /* multi entry vs multi stride >>> */ struct strmap Syscall_Map[] = { {"read", READ, 0}, {"write", WRITE, SY_WRITE}, #ifdef CRAY {"reada", READA, SY_ASYNC}, {"writea", WRITEA, SY_WRITE | SY_ASYNC}, #ifndef _CRAYMPP {"ssread", SSREAD, SY_SDS}, {"sswrite", SSWRITE, SY_WRITE | SY_SDS}, #endif {"listio", LISTIO, SY_ASYNC}, /* listio as 4 system calls */ {"lread", LREAD, 0}, {"lreada", LREADA, SY_ASYNC}, {"lwrite", LWRITE, SY_WRITE}, {"lwritea", LWRITEA, SY_WRITE | SY_ASYNC}, /* listio with nstrides > 1 */ {"lsread", LSREAD, 0}, {"lsreada", LSREADA, SY_ASYNC}, {"lswrite", LSWRITE, SY_WRITE}, {"lswritea", LSWRITEA, SY_WRITE | SY_ASYNC}, /* listio with nents > 1 */ {"leread", LEREAD, 0 | SY_NENT}, {"lereada", LEREADA, SY_ASYNC | SY_NENT}, {"lewrite", LEWRITE, SY_WRITE | SY_NENT}, {"lewritea", LEWRITEA, SY_WRITE | SY_ASYNC | SY_NENT}, /* listio with nents > 1 & nstrides > 1 */ /* all listio system calls under one name */ {"listio+", LREAD, 0}, {"listio+", LREADA, SY_ASYNC}, {"listio+", LWRITE, SY_WRITE}, {"listio+", LWRITEA, SY_WRITE | SY_ASYNC}, {"listio+", LSREAD, 0}, {"listio+", LSREADA, SY_ASYNC}, {"listio+", LSWRITE, SY_WRITE}, {"listio+", LSWRITEA, SY_WRITE | SY_ASYNC}, {"listio+", LEREAD, 0 | SY_NENT}, {"listio+", LEREADA, SY_ASYNC | SY_NENT}, {"listio+", LEWRITE, SY_WRITE | SY_NENT}, {"listio+", LEWRITEA, SY_WRITE | SY_ASYNC | SY_NENT}, #endif #ifdef sgi {"pread", PREAD}, {"pwrite", PWRITE, SY_WRITE}, {"aread", AREAD, SY_ASYNC}, {"awrite", AWRITE, SY_WRITE | SY_ASYNC}, #if 0 /* not written yet */ {"llread", LLREAD, 0}, {"llaread", LLAREAD, SY_ASYNC}, {"llwrite", LLWRITE, 0}, {"llawrite", LLAWRITE, SY_ASYNC}, #endif {"resvsp", RESVSP, SY_WRITE}, {"unresvsp", UNRESVSP, SY_WRITE}, {"reserve", RESVSP, SY_WRITE}, {"unreserve", UNRESVSP, SY_WRITE}, {"ffsync", DFFSYNC, SY_WRITE}, #endif /* SGI */ #ifndef CRAY {"readv", READV}, {"writev", WRITEV, SY_WRITE}, {"mmread", MMAPR}, {"mmwrite", MMAPW, SY_WRITE}, {"fsync2", FSYNC2, SY_WRITE}, {"fdatasync", FDATASYNC, SY_WRITE}, #endif {NULL, -1} }; /* * Map open flags (-f args) to values */ #define FLG_RAW 00001 struct strmap Flag_Map[] = { {"buffered", 0, 0}, {"sync", O_SYNC, 0}, #ifdef CRAY {"raw", O_RAW, FLG_RAW}, {"raw+wf", O_RAW | O_WELLFORMED, FLG_RAW}, {"raw+wf+ldraw", O_RAW | O_WELLFORMED | O_LDRAW, FLG_RAW}, {"raw+wf+ldraw+sync", O_RAW | O_WELLFORMED | O_LDRAW | O_SYNC, FLG_RAW}, #ifdef O_SSD {"ssd", O_SSD, FLG_RAW}, #endif #ifdef O_LDRAW {"ldraw", O_LDRAW, 0}, #endif #ifdef O_PARALLEL {"parallel", O_PARALLEL | O_RAW | O_WELLFORMED, FLG_RAW}, {"parallel+sync", O_PARALLEL | O_RAW | O_WELLFORMED | O_SYNC, FLG_RAW}, {"parallel+ldraw", O_PARALLEL | O_RAW | O_WELLFORMED | O_LDRAW, FLG_RAW}, {"parallel+ldraw+sync", O_PARALLEL | O_RAW | O_WELLFORMED | O_LDRAW | O_SYNC, FLG_RAW}, #endif #endif /* CRAY */ #ifdef sgi {"direct", O_DIRECT, FLG_RAW}, {"dsync", O_DSYNC}, /* affects writes */ {"rsync", O_RSYNC}, /* affects reads */ {"rsync+dsync", O_RSYNC | O_DSYNC}, #endif {NULL, -1} }; /* * Map file types to strings */ struct strmap Ftype_Map[] = { {"regular", S_IFREG}, {"blk-spec", S_IFBLK}, {"chr-spec", S_IFCHR}, {NULL, 0} }; /* * Misc declarations */ int Sds_Avail; char Byte_Patterns[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; int main(int argc, char **argv) { int rseed, outfd, infinite; time_t start_time; struct io_req req; umask(0); #ifdef CRAY Sds_Avail = sysconf(_SC_CRAY_SDS); #else Sds_Avail = 0; #endif TagName[0] = '\0'; parse_cmdline(argc, argv, OPTS); /* * Initialize output descriptor. */ if (!p_opt) { outfd = 1; } else { outfd = init_output(); } rseed = getpid(); random_range_seed(rseed); /* initialize random number generator */ /* * Print out startup information, unless we're running in quiet mode */ if (!q_opt) startup_info(stderr, rseed); { struct timeval ts; gettimeofday(&ts, NULL); start_time = ts.tv_sec; } /* * While iterations (or forever if Iterations == 0) - compute an * io request, and write the structure to the output descriptor */ infinite = !Iterations; struct timeval ts; gettimeofday(&ts, NULL); while (infinite || (!Time_Mode && Iterations--) || (Time_Mode && (ts.tv_sec - start_time <= Iterations))) { gettimeofday(&ts, NULL); memset(&req, 0, sizeof(struct io_req)); if (form_iorequest(&req) == -1) { fprintf(stderr, "iogen%s: form_iorequest() failed\n", TagName); continue; } req.r_magic = DOIO_MAGIC; if (write(outfd, (char *)&req, sizeof(req)) == -1) perror("Warning: Could not write"); } exit(0); } /* main */ void startup_info(FILE * stream, int seed) { char *value_to_string(), *type; int i; fprintf(stream, "\n"); fprintf(stream, "iogen%s starting up with the following:\n", TagName); fprintf(stream, "\n"); fprintf(stream, "Out-pipe: %s\n", p_opt ? Outpipe : "stdout"); if (Iterations) { fprintf(stream, "Iterations: %d", Iterations); if (Time_Mode) fprintf(stream, " seconds"); fprintf(stream, "\n"); } else { fprintf(stream, "Iterations: Infinite\n"); } fprintf(stream, "Seed: %d\n", seed); fprintf(stream, "Offset-Mode: %s\n", Offset_Mode->m_string); fprintf(stream, "Overlap Flag: %s\n", o_opt ? "on" : "off"); fprintf(stream, "Mintrans: %-11d (%d blocks)\n", Mintrans, (Mintrans + BSIZE - 1) / BSIZE); fprintf(stream, "Maxtrans: %-11d (%d blocks)\n", Maxtrans, (Maxtrans + BSIZE - 1) / BSIZE); if (!r_opt) fprintf(stream, "O_RAW/O_SSD Multiple: (Determined by device)\n"); else fprintf(stream, "O_RAW/O_SSD Multiple: %-11d (%d blocks)\n", Rawmult, (Rawmult + BSIZE - 1) / BSIZE); fprintf(stream, "Syscalls: "); for (i = 0; i < Nsyscalls; i++) fprintf(stream, "%s ", Syscall_List[i]->m_string); fprintf(stream, "\n"); fprintf(stream, "Aio completion types: "); for (i = 0; i < Naio_Strat_Types; i++) fprintf(stream, "%s ", Aio_Strat_List[i]->m_string); fprintf(stream, "\n"); if (Fileio) { fprintf(stream, "Flags: "); for (i = 0; i < Nflags; i++) fprintf(stream, "%s ", Flag_List[i]->m_string); fprintf(stream, "\n"); fprintf(stream, "\n"); fprintf(stream, "Test Files: \n"); fprintf(stream, "\n"); fprintf(stream, "Path Length iou raw iou file\n"); fprintf(stream, " (bytes) (bytes) (bytes) type\n"); fprintf(stream, "-----------------------------------------------------------------------------\n"); for (i = 0; i < Nfiles; i++) { type = value_to_string(Ftype_Map, File_List[i].f_type); fprintf(stream, "%-40s %12d %7d %7d %s\n", File_List[i].f_path, File_List[i].f_length, File_List[i].f_iou, File_List[i].f_riou, type); } } } /* * Initialize output descriptor. If going to stdout, its easy, * otherwise, attempt to create a FIFO on path Outpipe. Exit with an * error code if this cannot be done. */ int init_output(void) { int outfd; struct stat sbuf; if (stat(Outpipe, &sbuf) == -1) { if (errno == ENOENT) { if (mkfifo(Outpipe, 0666) == -1) { fprintf(stderr, "iogen%s: Could not mkfifo %s: %s\n", TagName, Outpipe, SYSERR); exit(2); } } else { fprintf(stderr, "iogen%s: Could not stat outpipe %s: %s\n", TagName, Outpipe, SYSERR); exit(2); } } else { if (!S_ISFIFO(sbuf.st_mode)) { fprintf(stderr, "iogen%s: Output file %s exists, but is not a FIFO\n", TagName, Outpipe); exit(2); } } if ((outfd = open(Outpipe, O_RDWR)) == -1) { fprintf(stderr, "iogen%s: Couldn't open outpipe %s with flags O_RDWR: %s\n", TagName, Outpipe, SYSERR); exit(2); } return (outfd); } /* * Main io generation function. form_iorequest() selects a system call to * do based on cmdline arguments, and proceeds to select parameters for that * system call. * * Returns 0 if req is filled in with a complete doio request, otherwise * returns -1. */ int form_iorequest(struct io_req *req) { int mult, offset = 0, length = 0, slength; int minlength, maxlength, laststart, lastend; int minoffset, maxoffset; int maxstride, nstrides; char pattern, *errp; struct strmap *flags, *sc, *aio_strat; struct file_info *fptr; #ifdef CRAY int opcode, cmd; #endif /* * Choose system call, flags, and file */ sc = Syscall_List[random_range(0, Nsyscalls - 1, 1, NULL)]; req->r_type = sc->m_value; #ifdef CRAY if (sc->m_value == LISTIO) { opcode = random_range(0, 1, 1, NULL) ? LO_READ : LO_WRITE; cmd = random_range(0, 1, 1, NULL) ? LC_START : LC_WAIT; } #endif if (sc->m_flags & SY_WRITE) pattern = Byte_Patterns[random_range (0, sizeof(Byte_Patterns) - 1, 1, NULL)]; else pattern = 0; #if CRAY /* * If sds io, simply choose a length (possibly pattern) and return */ if (sc->m_flags & SY_SDS) { req->r_data.ssread.r_nbytes = random_range(Mintrans, Maxtrans, BSIZE, NULL); if (sc->m_flags & SY_WRITE) req->r_data.sswrite.r_pattern = pattern; return 0; } #endif /* * otherwise, we're doing file io. Choose starting offset, length, * open flags, and possibly a pattern (for write/writea). */ fptr = &File_List[random_range(0, Nfiles - 1, 1, NULL)]; flags = Flag_List[random_range(0, Nflags - 1, 1, NULL)]; /* * Choose offset/length multiple. IO going to a device, or regular * IO that is O_RAW or O_SSD must be aligned on the file r_iou. Otherwise * it must be aligned on the regular iou (normally 1). */ if (fptr->f_type == S_IFREG && (flags->m_flags & FLG_RAW)) mult = fptr->f_riou; else mult = fptr->f_iou; /* * Choose offset and length. Both must be a multiple of mult */ /* * Choose length first - it must be a multiple of mult */ laststart = fptr->f_lastoffset; lastend = fptr->f_lastoffset + fptr->f_lastlength - 1; minlength = (Mintrans > mult) ? Mintrans : mult; switch (Offset_Mode->m_value) { case M_SEQUENTIAL: if (o_opt && lastend > laststart) offset = random_range(laststart, lastend, 1, NULL); else offset = lastend + 1; if (offset && (offset % mult)) offset += mult - (offset % mult); if (minlength > fptr->f_length - offset) offset = 0; maxlength = fptr->f_length - offset; if (maxlength > Maxtrans) maxlength = Maxtrans; length = random_range(minlength, maxlength, mult, &errp); if (errp != NULL) { fprintf(stderr, "iogen%s: random_range(%d, %d, %d) failed\n", TagName, minlength, maxlength, mult); return -1; } break; case M_REVERSE: maxlength = laststart; if (maxlength > Maxtrans) maxlength = Maxtrans; if (minlength > maxlength) { laststart = fptr->f_length; lastend = fptr->f_length; maxlength = Maxtrans; } length = random_range(minlength, maxlength, mult, &errp); if (errp != NULL) { fprintf(stderr, "iogen%s: random_range(%d, %d, %d) failed\n", TagName, minlength, maxlength, mult); return -1; } offset = laststart - length; if (o_opt && lastend > laststart) offset += random_range(1, lastend - laststart, 1, NULL); if (offset && (offset % mult)) offset -= offset % mult; break; case M_RANDOM: length = random_range(Mintrans, Maxtrans, mult, NULL); if (o_opt && lastend > laststart) { minoffset = laststart - length + 1; if (minoffset < 0) { minoffset = 0; } if (lastend + length > fptr->f_length) { maxoffset = fptr->f_length - length; } else { maxoffset = lastend; } } else { minoffset = 0; maxoffset = fptr->f_length - length; } if (minoffset < 0) minoffset = 0; offset = random_range(minoffset, maxoffset, mult, &errp); if (errp != NULL) { fprintf(stderr, "iogen%s: random_range(%d, %d, %d) failed\n", TagName, minoffset, maxoffset, mult); return -1; } } fptr->f_lastoffset = offset; fptr->f_lastlength = length; /* * Choose an async io completion strategy if necessary */ if (sc->m_flags & SY_ASYNC) aio_strat = Aio_Strat_List[random_range(0, Naio_Strat_Types - 1, 1, NULL)]; else aio_strat = NULL; /* * fill in specific syscall record data */ switch (sc->m_value) { case READ: case READA: strcpy(req->r_data.read.r_file, fptr->f_path); req->r_data.read.r_oflags = O_RDONLY | flags->m_value; req->r_data.read.r_offset = offset; req->r_data.read.r_nbytes = length; req->r_data.read.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0; req->r_data.read.r_aio_strat = (aio_strat == NULL) ? 0 : aio_strat->m_value; req->r_data.read.r_nstrides = 1; req->r_data.read.r_nent = 1; break; case WRITE: case WRITEA: strcpy(req->r_data.write.r_file, fptr->f_path); req->r_data.write.r_oflags = O_WRONLY | flags->m_value; req->r_data.write.r_offset = offset; req->r_data.write.r_nbytes = length; req->r_data.write.r_pattern = pattern; req->r_data.write.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0; req->r_data.write.r_aio_strat = (aio_strat == NULL) ? 0 : aio_strat->m_value; req->r_data.write.r_nstrides = 1; req->r_data.write.r_nent = 1; break; case READV: case AREAD: case PREAD: case WRITEV: case AWRITE: case PWRITE: case LREAD: case LREADA: case LWRITE: case LWRITEA: case RESVSP: case UNRESVSP: case DFFSYNC: case FSYNC2: case FDATASYNC: strcpy(req->r_data.io.r_file, fptr->f_path); req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_WRONLY : O_RDONLY) | flags-> m_value; req->r_data.io.r_offset = offset; req->r_data.io.r_nbytes = length; req->r_data.io.r_pattern = pattern; req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0; req->r_data.io.r_aio_strat = (aio_strat == NULL) ? 0 : aio_strat->m_value; req->r_data.io.r_nstrides = 1; req->r_data.io.r_nent = 1; break; case MMAPR: case MMAPW: strcpy(req->r_data.io.r_file, fptr->f_path); /* a subtle "feature" of mmap: a write-map requires the file open read/write */ req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_RDWR : O_RDONLY) | flags-> m_value; req->r_data.io.r_offset = offset; req->r_data.io.r_nbytes = length; req->r_data.io.r_pattern = pattern; req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0; req->r_data.io.r_aio_strat = (aio_strat == NULL) ? 0 : aio_strat->m_value; req->r_data.io.r_nstrides = 1; req->r_data.io.r_nent = 1; break; case LSREAD: case LSREADA: case LEREAD: case LEREADA: case LSWRITE: case LSWRITEA: case LEWRITE: case LEWRITEA: /* multi-strided */ strcpy(req->r_data.io.r_file, fptr->f_path); req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_WRONLY : O_RDONLY) | flags-> m_value; req->r_data.io.r_offset = offset; req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0; req->r_data.io.r_aio_strat = (aio_strat == NULL) ? 0 : aio_strat->m_value; req->r_data.io.r_pattern = pattern; /* multi-strided request... * random number of strides (1...MaxStrides) * length of stride must be > minlength * length of stride must be % mult * * maxstrides = min(length / mult, overall.max#strides) * nstrides = random # * while (length / nstrides < minlength) * nstrides = new random # */ maxstride = length / mult; if (maxstride > Maxstrides) maxstride = Maxstrides; if (!Minstrides) Minstrides = 1; nstrides = random_range(Minstrides, maxstride, 1, &errp); if (errp != NULL) { fprintf(stderr, "iogen%s: random_range(%d, %d, %d) failed\n", TagName, Minstrides, maxstride, 1); return -1; } slength = length / nstrides; if (slength % mult != 0) { if (mult > slength) { slength = mult; } else { slength -= slength % mult; } nstrides = length / slength; if (nstrides > Maxstrides) nstrides = Maxstrides; } req->r_data.io.r_nbytes = slength; if (sc->m_flags & SY_NENT) { req->r_data.io.r_nstrides = 1; req->r_data.io.r_nent = nstrides; } else { req->r_data.io.r_nstrides = nstrides; req->r_data.io.r_nent = 1; } break; case LISTIO: #ifdef CRAY strcpy(req->r_data.listio.r_file, fptr->f_path); req->r_data.listio.r_offset = offset; req->r_data.listio.r_cmd = cmd; req->r_data.listio.r_aio_strat = (aio_strat == NULL) ? 0 : aio_strat->m_value; req->r_data.listio.r_filestride = 0; req->r_data.listio.r_memstride = 0; req->r_data.listio.r_opcode = opcode; req->r_data.listio.r_nstrides = 1; req->r_data.listio.r_nbytes = length; req->r_data.listio.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0; if (opcode == LO_WRITE) { req->r_data.listio.r_pattern = pattern; req->r_data.listio.r_oflags = O_WRONLY | flags->m_value; } else { req->r_data.listio.r_oflags = O_RDONLY | flags->m_value; } #endif break; } return 0; } /* * Get information about a file that iogen uses to choose io length and * offset. Information gathered is file length, iounit, and raw iounit. * For regurlar files, iounit is 1, and raw iounit is the iounit of the * device on which the file resides. For block/character special files * the iounit and raw iounit are both the iounit of the device. * * Note: buffered and osync io must be iounit aligned * raw and ossd io must be raw iounit aligned */ int get_file_info(struct file_info *rec) { struct stat sbuf; #ifdef CRAY struct lk_device_info dinfo; #endif #ifdef sgi int fd; struct dioattr finfo; #endif /* * Figure out if the files is regular, block or character special. Any * other type is an error. */ if (stat(rec->f_path, &sbuf) == -1) { fprintf(stderr, "iogen%s: get_file_info(): Could not stat() %s: %s\n", TagName, rec->f_path, SYSERR); return -1; } #if _CRAY2 if ((!S_ISREG(sbuf.st_mode)) || strncmp(rec->f_path, "/dev/", 5) == 0) { fprintf(stderr, "iogen%s: device level io not supported on cray2\n", TagName); return -1; } #endif rec->f_type = sbuf.st_mode & S_IFMT; /* * If regular, iou is 1, and we must figure out the device on * which the file resides. riou is the iou (logical sector size) of * this device. */ if (S_ISREG(sbuf.st_mode)) { rec->f_iou = 1; rec->f_length = sbuf.st_size; /* * If -r used, take Rawmult as the raw/ssd io multiple. Otherwise * attempt to determine it by looking at the device the file * resides on. */ if (r_opt) { rec->f_riou = Rawmult; return 0; } #ifdef CRAY if (lk_rawdev(rec->f_path, dinfo.path, sizeof(dinfo.path), 0) == -1) return -1; if (lk_devinfo(&dinfo, 0) == -1) { /* can't get raw I/O unit -- use stat to fudge it */ rec->f_riou = sbuf.st_blksize; } else { rec->f_riou = ctob(dinfo.iou); } #endif #ifdef linux rec->f_riou = BSIZE; #endif #ifdef sgi if ((fd = open(rec->f_path, O_RDWR | O_DIRECT, 0)) != -1) { if (fcntl(fd, F_DIOINFO, &finfo) != -1) { rec->f_riou = finfo.d_miniosz; } else { fprintf(stderr, "iogen%s: Error %s (%d) getting direct I/O info of file %s\n", TagName, strerror(errno), errno, rec->f_path); } close(fd); } else { rec->f_riou = BBSIZE; } #endif /* SGI */ } else { #ifdef CRAY /* * Otherwise, file is a device. Use lk_devinfo() to get its logical * sector size. This is the iou and riou */ strcpy(dinfo.path, rec->f_path); if (lk_devinfo(&dinfo, 0) == -1) { fprintf(stderr, "iogen%s: %s: %s\n", TagName, Lk_err_func, Lk_err_mesg); return -1; } rec->f_iou = ctob(dinfo.iou); rec->f_riou = ctob(dinfo.iou); rec->f_length = ctob(dinfo.length); #else #ifdef sgi rec->f_riou = BBSIZE; rec->f_length = BBSIZE; #else rec->f_riou = BSIZE; rec->f_length = BSIZE; #endif /* sgi */ #endif /* CRAY */ } return 0; } /* * Create file path as nbytes long. If path exists, the file will either be * extended or truncated to be nbytes long. Returns final size of file, * or -1 if there was a failure. */ int create_file(char *path, int nbytes) { int fd, rval; char c; struct stat sbuf; #ifdef sgi int nb; struct flock f; struct fsxattr xattr; struct dioattr finfo; char *b, *buf; #endif errno = 0; rval = stat(path, &sbuf); if (rval == -1) { if (errno == ENOENT) { sbuf.st_size = 0; } else { fprintf(stderr, "iogen%s: Could not stat file %s: %s (%d)\n", TagName, path, SYSERR, errno); return -1; } } else { if (!S_ISREG(sbuf.st_mode)) { fprintf(stderr, "iogen%s: file %s exists, but is not a regular file - cannot modify length\n", TagName, path); return -1; } } if (sbuf.st_size == nbytes) return nbytes; Oflags |= O_CREAT | O_WRONLY; if ((fd = open(path, Oflags, 0666)) == -1) { fprintf(stderr, "iogen%s: Could not create/open file %s: %s (%d)\n", TagName, path, SYSERR, errno); return -1; } /* * Truncate file if it is longer than nbytes, otherwise attempt to * pre-allocate file blocks. */ if (sbuf.st_size > nbytes) { if (ftruncate(fd, nbytes) == -1) { fprintf(stderr, "iogen%s: Could not ftruncate() %s to %d bytes: %s (%d)\n", TagName, path, nbytes, SYSERR, errno); close(fd); return -1; } } else { #ifdef sgi /* * The file must be designated as Real-Time before any data * is allocated to it. * */ if (Orealtime != 0) { memset(&xattr, 0x00, sizeof(xattr)); xattr.fsx_xflags = XFS_XFLAG_REALTIME; /*fprintf(stderr, "set: fsx_xflags = 0x%x\n", xattr.fsx_xflags); */ if (fcntl(fd, F_FSSETXATTR, &xattr) == -1) { fprintf(stderr, "iogen%s: Error %s (%d) setting XFS XATTR->Realtime on file %s\n", TagName, SYSERR, errno, path); close(fd); return -1; } #ifdef DEBUG if (fcntl(fd, F_FSGETXATTR, &xattr) == -1) { fprintf(stderr, "iogen%s: Error getting realtime flag %s (%d)\n", TagName, SYSERR, errno); close(fd); return -1; } else { fprintf(stderr, "get: fsx_xflags = 0x%x\n", xattr.fsx_xflags); } #endif } /* * Reserve space with F_RESVSP * * Failure is ignored since F_RESVSP only works on XFS and the * filesystem could be on EFS or NFS */ if (Oreserve) { f.l_whence = SEEK_SET; f.l_start = 0; f.l_len = nbytes; /*fprintf(stderr, "create_file: fcntl(%d, F_RESVSP, { %d, %lld, %lld })\n", fd, f.l_whence, (long long)f.l_start, (long long)f.l_len); */ /* non-zeroing reservation */ if (fcntl(fd, F_RESVSP, &f) == -1) { fprintf(stderr, "iogen%s: Could not fcntl(F_RESVSP) %d bytes in file %s: %s (%d)\n", TagName, nbytes, path, SYSERR, errno); close(fd); return -1; } } if (Oallocate) { /* F_ALLOCSP allocates from the start of the file to l_start */ f.l_whence = SEEK_SET; f.l_start = nbytes; f.l_len = 0; /*fprintf(stderr, "create_file: fcntl(%d, F_ALLOCSP, { %d, %lld, %lld })\n", fd, f.l_whence, (long long)f.l_start, (long long)f.l_len); */ /* zeroing reservation */ if (fcntl(fd, F_ALLOCSP, &f) == -1) { fprintf(stderr, "iogen%s: Could not fcntl(F_ALLOCSP) %d bytes in file %s: %s (%d)\n", TagName, nbytes, path, SYSERR, errno); close(fd); return -1; } } #endif /* sgi */ /* * Write a byte at the end of file so that stat() sets the right * file size. */ #ifdef sgi if (Owrite == 2) { close(fd); if ((fd = open(path, O_CREAT | O_RDWR | O_DIRECT, 0)) != -1) { if (fcntl(fd, F_DIOINFO, &finfo) == -1) { fprintf(stderr, "iogen%s: Error %s (%d) getting direct I/O info for file %s\n", TagName, SYSERR, errno, path); return -1; } else { /*fprintf(stderr, "%s: miniosz=%d\n", path, finfo.d_miniosz); */ } } else { fprintf(stderr, "iogen%s: Error %s (%d) opening file %s with flags O_CREAT|O_RDWR|O_DIRECT\n", TagName, SYSERR, errno, path); return -1; } /* * nb is nbytes adjusted down by an even d_miniosz block * * Note: the first adjustment can cause iogen to print a warning * about not being able to create a file of <nbytes> length, * since the file will be shorter. */ nb = nbytes - finfo.d_miniosz; nb = nb - nb % finfo.d_miniosz; /*fprintf(stderr, "create_file_ow2: lseek(%d, %d {%d %d}, SEEK_SET)\n", fd, nb, nbytes, finfo.d_miniosz); */ if (lseek(fd, nb, SEEK_SET) == -1) { fprintf(stderr, "iogen%s: Could not lseek() to EOF of file %s: %s (%d)\n\tactual offset %d file size goal %d miniosz %lld\n", TagName, path, SYSERR, errno, nb, nbytes, (long long)finfo.d_miniosz); close(fd); return -1; } b = buf = malloc(finfo.d_miniosz + finfo.d_mem); if (((long)buf % finfo.d_mem != 0)) { buf += finfo.d_mem - ((long)buf % finfo.d_mem); } memset(buf, 0, finfo.d_miniosz); if ((rval = write(fd, buf, finfo.d_miniosz)) != finfo.d_miniosz) { fprintf(stderr, "iogen%s: Could not write %d byte length file %s: %s (%d)\n", TagName, nb, path, SYSERR, errno); fprintf(stderr, "\twrite(%d, 0x%lx, %d) = %d\n", fd, (long)buf, finfo.d_miniosz, rval); fprintf(stderr, "\toffset %d file size goal %d, miniosz=%d\n", nb, nbytes, finfo.d_miniosz); close(fd); return -1; } free(b); } else #endif /* sgi */ if (Owrite) { /*fprintf(stderr, "create_file_Owrite: lseek(%d, %d {%d}, SEEK_SET)\n", fd, nbytes-1, nbytes); */ if (lseek(fd, nbytes - 1, SEEK_SET) == -1) { fprintf(stderr, "iogen%s: Could not lseek() to EOF in file %s: %s (%d)\n\toffset goal %d\n", TagName, path, SYSERR, errno, nbytes - 1); close(fd); return -1; } if ((rval = write(fd, &c, 1)) != 1) { fprintf(stderr, "iogen%s: Could not create a %d byte length file %s: %s (%d)\n", TagName, nbytes, path, SYSERR, errno); fprintf(stderr, "\twrite(%d, 0x%lx, %d) = %d\n", fd, (long)&c, 1, rval); fprintf(stderr, "\toffset %d file size goal %d\n", nbytes - 1, nbytes); close(fd); return -1; } } } fstat(fd, &sbuf); close(fd); return sbuf.st_size; } /* * Function to convert a string to its corresponding value in a strmap array. * If the string is not found in the array, the value corresponding to the * NULL string (the last element in the array) is returned. */ int str_to_value(struct strmap *map, char *str) { struct strmap *mp; for (mp = map; mp->m_string != NULL; mp++) if (strcmp(mp->m_string, str) == 0) break; return mp->m_value; } /* * Function to convert a string to its corresponding entry in a strmap array. * If the string is not found in the array, a NULL is returned. */ struct strmap *str_lookup(struct strmap *map, char *str) { struct strmap *mp; for (mp = map; mp->m_string != NULL; mp++) if (strcmp(mp->m_string, str) == 0) break; return ((mp->m_string == NULL) ? NULL : mp); } /* * Function to convert a value to its corresponding string in a strmap array. * If the value is not found in the array, NULL is returned. */ char *value_to_string(struct strmap *map, int val) { struct strmap *mp; for (mp = map; mp->m_string != NULL; mp++) if (mp->m_value == val) break; return mp->m_string; } /* * Interpret cmdline options/arguments. Exit with 1 if something on the * cmdline isn't kosher. */ int parse_cmdline(int argc, char **argv, char *opts) { int o, len, nb, format_error; struct strmap *flgs, *sc; char *file, *cp, ch; extern int opterr; extern int optind; extern char *optarg; struct strmap *mp; struct file_info *fptr; int nopenargs; char *openargs[5]; /* Flags, cbits, cblks */ char *errmsg; int str_to_int(); opterr = 0; #ifndef linux char *ranges; struct strmap *type; #endif while ((o = getopt(argc, argv, opts)) != EOF) { switch ((char)o) { case 'a': #ifdef linux fprintf(stderr, "iogen%s: Unrecognized option -a on this platform\n", TagName); exit(2); #else cp = strtok(optarg, ","); while (cp != NULL) { if ((type = str_lookup(Aio_Strat_Map, cp)) == NULL) { fprintf(stderr, "iogen%s: Unrecognized aio completion strategy: %s\n", TagName, cp); exit(2); } Aio_Strat_List[Naio_Strat_Types++] = type; cp = strtok(NULL, ","); } a_opt++; #endif break; case 'f': cp = strtok(optarg, ","); while (cp != NULL) { if ((flgs = str_lookup(Flag_Map, cp)) == NULL) { fprintf(stderr, "iogen%s: Unrecognized flags: %s\n", TagName, cp); exit(2); } cp = strtok(NULL, ","); #ifdef O_SSD if (flgs->m_value & O_SSD && !Sds_Avail) { fprintf(stderr, "iogen%s: Warning - no sds available, ignoring ssd flag\n", TagName); continue; } #endif Flag_List[Nflags++] = flgs; } f_opt++; break; case 'h': help(stdout); exit(0); break; case 'i': format_error = 0; switch (sscanf(optarg, "%i%c", &Iterations, &ch)) { case 1: Time_Mode = 0; break; case 2: if (ch == 's') Time_Mode = 1; else format_error = 1; break; default: format_error = 1; } if (Iterations < 0) format_error = 1; if (format_error) { fprintf(stderr, "iogen%s: Illegal -i arg (%s): Must be of the format: number[s]\n", TagName, optarg); fprintf(stderr, " where 'number' is >= 0\n"); exit(1); } i_opt++; break; case 'L': #ifdef linux fprintf(stderr, "iogen%s: Unrecognized option -L on this platform\n", TagName); exit(2); #else if (parse_ranges(optarg, 1, 255, 1, NULL, &ranges, &errmsg) == -1) { fprintf(stderr, "iogen%s: error parsing listio range '%s': %s\n", TagName, optarg, errmsg); exit(1); } Minstrides = range_min(ranges, 0); Maxstrides = range_max(ranges, 0); free(ranges); L_opt++; #endif break; case 'm': if ((Offset_Mode = str_lookup(Omode_Map, optarg)) == NULL) { fprintf(stderr, "iogen%s: Illegal -m arg (%s)\n", TagName, optarg); exit(1); } m_opt++; break; case 'N': sprintf(TagName, "(%.39s)", optarg); break; case 'o': o_opt++; break; case 'O': nopenargs = string_to_tokens(optarg, openargs, 4, ":/"); #ifdef CRAY if (nopenargs) sscanf(openargs[1], "%i", &Ocbits); if (nopenargs > 1) sscanf(openargs[2], "%i", &Ocblks); Oflags = parse_open_flags(openargs[0], &errmsg); if (Oflags == -1) { fprintf(stderr, "iogen%s: -O %s error: %s\n", TagName, optarg, errmsg); exit(1); } #endif #ifdef linux Oflags = parse_open_flags(openargs[0], &errmsg); if (Oflags == -1) { fprintf(stderr, "iogen%s: -O %s error: %s\n", TagName, optarg, errmsg); exit(1); } #endif #ifdef sgi if (!strcmp(openargs[0], "realtime")) { /* * -O realtime:extsize */ Orealtime = 1; if (nopenargs > 1) sscanf(openargs[1], "%i", &Oextsize); else Oextsize = 0; } else if (!strcmp(openargs[0], "allocate") || !strcmp(openargs[0], "allocsp")) { /* * -O allocate */ Oreserve = 0; Oallocate = 1; } else if (!strcmp(openargs[0], "reserve")) { /* * -O [no]reserve */ Oallocate = 0; Oreserve = 1; } else if (!strcmp(openargs[0], "noreserve")) { /* Oreserve=1 by default; this clears that default */ Oreserve = 0; } else if (!strcmp(openargs[0], "nowrite")) { /* Owrite=1 by default; this clears that default */ Owrite = 0; } else if (!strcmp(openargs[0], "direct")) { /* this means "use direct i/o to preallocate file" */ Owrite = 2; } else { fprintf(stderr, "iogen%s: Error: -O %s error: unrecognized option\n", TagName, openargs[0]); exit(1); } #endif O_opt++; break; case 'p': Outpipe = optarg; p_opt++; break; case 'r': if ((Rawmult = bytes_by_prefix(optarg)) == -1 || Rawmult < 11 || Rawmult % BSIZE) { fprintf(stderr, "iogen%s: Illegal -r arg (%s). Must be > 0 and multipe of BSIZE (%d)\n", TagName, optarg, BSIZE); exit(1); } r_opt++; break; case 's': cp = strtok(optarg, ","); while (cp != NULL) { if ((sc = str_lookup(Syscall_Map, cp)) == NULL) { fprintf(stderr, "iogen%s: Unrecognized syscall: %s\n", TagName, cp); exit(2); } do { /* >>> sc->m_flags & FLG_SDS */ if (sc->m_value != SSREAD && sc->m_value != SSWRITE) Fileio++; Syscall_List[Nsyscalls++] = sc; } while ((sc = str_lookup(++sc, cp)) != NULL); cp = strtok(NULL, ","); } s_opt++; break; case 't': if ((Mintrans = bytes_by_prefix(optarg)) == -1) { fprintf(stderr, "iogen%s: Illegal -t arg (%s): Must have the form num[bkm]\n", TagName, optarg); exit(1); } t_opt++; break; case 'T': if ((Maxtrans = bytes_by_prefix(optarg)) == -1) { fprintf(stderr, "iogen%s: Illegal -T arg (%s): Must have the form num[bkm]\n", TagName, optarg); exit(1); } T_opt++; break; case 'q': q_opt++; break; case '?': usage(stderr); exit(1); } } /* * Supply defaults */ if (!L_opt) { Minstrides = 1; Maxstrides = 255; } if (!m_opt) Offset_Mode = str_lookup(Omode_Map, "sequential"); if (!i_opt) Iterations = 0; if (!t_opt) Mintrans = 1; if (!T_opt) Maxtrans = 256 * BSIZE; if (!O_opt) Oflags = Ocbits = Ocblks = 0; /* * Supply default async io completion strategy types. */ if (!a_opt) { for (mp = Aio_Strat_Map; mp->m_string != NULL; mp++) { Aio_Strat_List[Naio_Strat_Types++] = mp; } } /* * Supply default syscalls. Default is read,write,reada,writea,listio. */ if (!s_opt) { Nsyscalls = 0; Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "read"); Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "write"); #ifdef CRAY Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "reada"); Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "writea"); Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lread"); Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lreada"); Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lwrite"); Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lwritea"); #endif #ifdef sgi Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "pread"); Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "pwrite"); /*Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "aread"); */ /*Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "awrite"); */ #endif #ifndef CRAY Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "readv"); Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "writev"); Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "mmread"); Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "mmwrite"); #endif Fileio = 1; } if (Fileio && (argc - optind < 1)) { fprintf(stderr, "iogen%s: No files specified on the cmdline\n", TagName); exit(1); } /* * Supply default file io flags - defaut is 'buffered,raw,sync,ldraw'. */ if (!f_opt && Fileio) { Nflags = 0; Flag_List[Nflags++] = str_lookup(Flag_Map, "buffered"); Flag_List[Nflags++] = str_lookup(Flag_Map, "sync"); #ifdef CRAY Flag_List[Nflags++] = str_lookup(Flag_Map, "raw+wf"); Flag_List[Nflags++] = str_lookup(Flag_Map, "ldraw"); #endif #ifdef sgi /* Warning: cannot mix direct i/o with others! */ Flag_List[Nflags++] = str_lookup(Flag_Map, "dsync"); Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync"); /* Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync+sync"); */ /* Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync+dsync"); */ #endif } if (Fileio) { if (optind >= argc) { fprintf(stderr, "iogen%s: No files listed on the cmdline\n", TagName); exit(1); } /* * Initialize File_List[] - only necessary if doing file io. First * space for the File_List array, then fill it in. */ File_List = malloc((argc - optind) * sizeof(struct file_info)); if (File_List == NULL) { fprintf(stderr, "iogen%s: Could not malloc space for %d file_info structures\n", TagName, argc - optind); exit(2); } memset(File_List, 0, (argc - optind) * sizeof(struct file_info)); Nfiles = 0; while (optind < argc) { len = -1; /* * Pick off leading len: if it's there and create/extend/trunc * the file to the desired length. Otherwise, just make sure * the file is accessable. */ if ((cp = strchr(argv[optind], ':')) != NULL) { *cp = '\0'; if ((len = bytes_by_prefix(argv[optind])) == -1) { fprintf(stderr, "iogen%s: illegal file length (%s) for file %s\n", TagName, argv[optind], cp + 1); exit(2); } *cp = ':'; file = cp + 1; if (strlen(file) > MAX_FNAME_LENGTH) { fprintf(stderr, "iogen%s: Max fname length is %d chars - ignoring file %s\n", TagName, MAX_FNAME_LENGTH, file); optind++; continue; } nb = create_file(file, len); if (nb < len) { fprintf(stderr, "iogen%s warning: Couldn't create file %s of %d bytes\n", TagName, file, len); if (nb <= 0) { optind++; continue; } } } else { file = argv[optind]; if (access(file, R_OK | W_OK) == -1) { fprintf(stderr, "iogen%s: file %s cannot be accessed for reading and/or writing: %s (%d)\n", TagName, file, SYSERR, errno); exit(2); } } /* * get per-file information */ fptr = &File_List[Nfiles]; if (file[0] == '/') { strcpy(fptr->f_path, file); } else { if (getcwd (fptr->f_path, sizeof(fptr->f_path) - 1) == NULL) perror ("Could not get current working directory"); strcat(fptr->f_path, "/"); strcat(fptr->f_path, file); } if (get_file_info(fptr) == -1) { fprintf(stderr, "iogen%s warning: Error getting file info for %s\n", TagName, file); } else { /* * If the file length is smaller than our min transfer size, * ignore it. */ if (fptr->f_length < Mintrans) { fprintf(stderr, "iogen%s warning: Ignoring file %s\n", TagName, fptr->f_path); fprintf(stderr, " length (%d) is < min transfer size (%d)\n", fptr->f_length, Mintrans); optind++; continue; } /* * If the file length is smaller than our max transfer size, * ignore it. */ if (fptr->f_length < Maxtrans) { fprintf(stderr, "iogen%s warning: Ignoring file %s\n", TagName, fptr->f_path); fprintf(stderr, " length (%d) is < max transfer size (%d)\n", fptr->f_length, Maxtrans); optind++; continue; } if (fptr->f_length > 0) { switch (Offset_Mode->m_value) { case M_SEQUENTIAL: fptr->f_lastoffset = 0; fptr->f_lastlength = 0; break; case M_REVERSE: fptr->f_lastoffset = fptr->f_length; fptr->f_lastlength = 0; break; case M_RANDOM: fptr->f_lastoffset = fptr->f_length / 2; fptr->f_lastlength = 0; break; } Nfiles++; } } optind++; } if (Nfiles == 0) { fprintf(stderr, "iogen%s: Could not create, or gather info for any test files\n", TagName); exit(2); } } return 0; } int help(FILE * stream) { usage(stream); fprintf(stream, "\n"); #ifndef linux fprintf(stream, "\t-a aio_type,... Async io completion types to choose. Supported types\n"); #ifdef CRAY #if _UMK || RELEASE_LEVEL >= 8000 fprintf(stream, "\t are: poll, signal, recall, recalla, and recalls.\n"); #else fprintf(stream, "\t are: poll, signal, recalla, and recalls.\n"); #endif #else fprintf(stream, "\t are: poll, signal, suspend, and callback.\n"); #endif fprintf(stream, "\t Default is all of the above.\n"); #else /* !linux */ fprintf(stream, "\t-a (Not used on Linux).\n"); #endif /* !linux */ fprintf(stream, "\t-f flag,... Flags to use for file IO. Supported flags are\n"); #ifdef CRAY fprintf(stream, "\t raw, ssd, buffered, ldraw, sync,\n"); fprintf(stream, "\t raw+wf, raw+wf+ldraw, raw+wf+ldraw+sync,\n"); fprintf(stream, "\t and parallel (unicos/mk on MPP only).\n"); fprintf(stream, "\t Default is 'raw,ldraw,sync,buffered'.\n"); #else #ifdef sgi fprintf(stream, "\t buffered, direct, sync, dsync, rsync,\n"); fprintf(stream, "\t rsync+dsync.\n"); fprintf(stream, "\t Default is 'buffered,sync,dsync,rsync'.\n"); #else fprintf(stream, "\t buffered, sync.\n"); fprintf(stream, "\t Default is 'buffered,sync'.\n"); #endif /* sgi */ #endif /* CRAY */ fprintf(stream, "\t-h This help.\n"); fprintf(stream, "\t-i iterations[s] # of requests to generate. 0 means causes iogen\n"); fprintf(stream, "\t to run until it's killed. If iterations is suffixed\n"); fprintf(stream, "\t with 's', then iterations is the number of seconds\n"); fprintf(stream, "\t that iogen should run for. Default is '0'.\n"); #ifndef linux fprintf(stream, "\t-L min:max listio nstrides / nrequests range\n"); #else fprintf(stream, "\t-L (Not used on Linux).\n"); #endif /* !linux */ fprintf(stream, "\t-m offset-mode The mode by which iogen chooses the offset for\n"); fprintf(stream, "\t consectutive transfers within a given file.\n"); fprintf(stream, "\t Allowed values are 'random', 'sequential',\n"); fprintf(stream, "\t and 'reverse'.\n"); fprintf(stream, "\t sequential is the default.\n"); fprintf(stream, "\t-N tagname Tag name, for Monster.\n"); fprintf(stream, "\t-o Form overlapping consecutive requests.\n"); fprintf(stream, "\t-O Open flags for creating files\n"); #ifdef CRAY fprintf(stream, "\t {O_PLACE,O_BIG,etc}[:CBITS[:CBLKS]]\n"); #endif #ifdef sgi fprintf(stream, "\t realtime:extsize - put file on real-time volume\n"); fprintf(stream, "\t allocate - allocate space with F_ALLOCSP\n"); fprintf(stream, "\t reserve - reserve space with F_RESVSP (default)\n"); fprintf(stream, "\t noreserve - do not reserve with F_RESVSP\n"); fprintf(stream, "\t direct - use O_DIRECT I/O to write to the file\n"); #endif #ifdef linux fprintf(stream, "\t {O_SYNC,etc}\n"); #endif fprintf(stream, "\t-p Output pipe. Default is stdout.\n"); fprintf(stream, "\t-q Quiet mode. Normally iogen spits out info\n"); fprintf(stream, "\t about test files, options, etc. before starting.\n"); fprintf(stream, "\t-s syscall,... Syscalls to do. Supported syscalls are\n"); #ifdef sgi fprintf(stream, "\t read, write, pread, pwrite, readv, writev\n"); fprintf(stream, "\t aread, awrite, resvsp, unresvsp, ffsync,\n"); fprintf(stream, "\t mmread, mmwrite, fsync2, fdatasync,\n"); fprintf(stream, "\t Default is 'read,write,pread,pwrite,readv,writev,mmread,mmwrite'.\n"); #endif #ifdef CRAY fprintf(stream, "\t read, write, reada, writea, listio,\n"); fprintf(stream, "\t ssread (PVP only), and sswrite (PVP only).\n"); fprintf(stream, "\t Default is 'read,write,reada,writea,listio'.\n"); #endif #ifdef linux fprintf(stream, "\t read, write, readv, writev,\n"); fprintf(stream, "\t mmread, mmwrite, fsync2, fdatasync,\n"); fprintf(stream, "\t Default is 'read,write,readv,writev,mmread,mmwrite'.\n"); #endif fprintf(stream, "\t-t mintrans Min transfer length\n"); fprintf(stream, "\t-T maxtrans Max transfer length\n"); fprintf(stream, "\n"); fprintf(stream, "\t[len:]file,... Test files to do IO against (note ssread/sswrite\n"); fprintf(stream, "\t don't need a test file). The len: syntax\n"); fprintf(stream, "\t informs iogen to first create/expand/truncate the\n"); fprintf(stream, "\t to the desired length.\n"); fprintf(stream, "\n"); fprintf(stream, "\tNote: The ssd flag causes sds transfers to also be done.\n"); fprintf(stream, "\t To totally eliminate sds transfers, you must eleminate sds\n"); fprintf(stream, "\t from the flags (-f) and ssread,ssrite from the syscalls (-s)\n"); fprintf(stream, "\tThe mintrans, maxtrans, and len: parameters are numbers of the\n"); fprintf(stream, "\tform [0-9]+[bkm]. The optional trailing b, k, or m multiplies\n"); fprintf(stream, "\tthe number by blocks, kilobytes, or megabytes. If no trailing\n"); fprintf(stream, "\tmultiplier is present, the number is interpreted as bytes\n"); return 0; } /* * Obvious - usage clause */ int usage(FILE * stream) { fprintf(stream, "usage%s: iogen [-hoq] [-a aio_type,...] [-f flag[,flag...]] [-i iterations] [-p outpipe] [-m offset-mode] [-s syscall[,syscall...]] [-t mintrans] [-T maxtrans] [ -O file-create-flags ] [[len:]file ...]\n", TagName); return 0; }