7a2009268ca04b6786e192a783c36dda8b8c855f
[xfstests-dev.git] / ltp / iogen.c
1 /*
2  * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
3  * 
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  * 
8  * This program is distributed in the hope that it would be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  * 
12  * Further, this software is distributed without any warranty that it is
13  * free of the rightful claim of any third person regarding infringement
14  * or the like.  Any license provided herein, whether implied or
15  * otherwise, applies only to this software file.  Patent licenses, if
16  * any, provided herein do not apply to combinations of this program with
17  * other software, or any other product whatsoever.
18  * 
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write the Free Software Foundation, Inc., 59
21  * Temple Place - Suite 330, Boston MA 02111-1307, USA.
22  * 
23  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24  * Mountain View, CA  94043, or:
25  * 
26  * http://www.sgi.com 
27  * 
28  * For further information regarding this notice, see: 
29  * 
30  * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
31  */
32 /*
33  * iogen - a tool for generating file/sds io for a doio process
34  */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <signal.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <signal.h>
44 #include <time.h>
45 #include <sys/param.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/sysmacros.h>
49 #ifdef CRAY
50 #include <sys/file.h>
51 #include <sys/iosw.h>
52 #include <sys/listio.h>
53 #endif
54 #ifdef sgi
55 #include <sys/statvfs.h>
56 #include <sys/fs/xfs_itable.h>
57 #endif
58
59 #ifndef NO_XFS
60 #include <xfs/libxfs.h>
61 #endif
62
63 #ifdef CRAY
64 #include "libkern.h"
65 #endif
66
67 #include "doio.h"
68 #include "str_to_bytes.h"
69 #include "string_to_tokens.h"
70 #include "open_flags.h"
71 #include "random_range.h"
72
73 #ifndef BSIZE
74 #define BSIZE 512
75 #endif
76
77 #define RAW_IO(_flags_) ((_flags_) & (O_RAW | O_SSD))
78
79 #define SYSERR  strerror(errno)
80
81 /*
82  * Structure for retaining test file information
83  */
84
85 struct file_info {
86         char    f_path[MAX_FNAME_LENGTH+1]; /* file name (full path)    */
87         int     f_length;       /* length in bytes                      */
88         int     f_iou;          /* file iounit                          */
89         int     f_riou;         /* file raw iounit (for O_RAW/O_SSD)    */
90         int     f_dalign;       /* direct I/O alignment                 */
91         int     f_nextoff;      /* offset of end of last io operation   */
92         int     f_type;         /* file type S_IFREG, etc...            */
93         int     f_lastoffset;   /* offset of last io operation          */
94         int     f_lastlength;   /* length of last io operation          */
95 };
96
97 /*
98  * Simple structure for associating strings with values - useful for converting
99  * cmdline args to internal values, as well as printing internal values in
100  * a human readable form.
101  */
102
103 struct strmap {
104         char    *m_string;
105         int     m_value;
106         int     m_flags;
107 };
108
109 /*
110  * Declare cmdline option flags/variables initialized in parse_cmdline()
111  */
112
113 #define OPTS    "a:dhf:i:L:m:op:qr:s:t:T:O:N:"
114
115 int     a_opt = 0;              /* async io comp. types supplied            */
116 int     o_opt = 0;              /* form overlapping requests                */
117 int     f_opt = 0;              /* test flags                               */
118 int     i_opt = 0;              /* iterations - 0 implies infinite          */
119 int     L_opt = 0;              /* listio min-max nstrides & nents          */
120 int     m_opt = 0;              /* offset mode                              */
121 int     O_opt = 0;              /* file creation Open flags                 */
122 int     p_opt = 0;              /* output pipe - default is stdout          */
123 int     r_opt = 0;              /* specify raw io multiple instead of       */
124                                 /* getting it from the mounted on device.   */
125                                 /* Only applies to regular files.           */
126 int     s_opt = 0;              /* syscalls                                 */
127 int     t_opt = 0;              /* min transfer size (bytes)                */
128 int     T_opt = 0;              /* max transfer size (bytes)                */
129 int     q_opt = 0;              /* quiet operation on startup               */
130 char    TagName[40];            /* name of this iogen (see Monster)         */
131 struct  strmap *Offset_Mode;    /* M_SEQUENTIAL, M_RANDOM, etc.             */
132 int     Iterations;             /* # requests to generate (0 --> infinite)  */
133 int     Time_Mode = 0;          /* non-zero if Iterations is in seconds     */
134                                 /* (ie. -i arg was suffixed with 's')       */
135 char    *Outpipe;               /* Pipe to write output to if p_opt         */
136 int     Mintrans;               /* min io transfer size                     */
137 int     Maxtrans;               /* max io transfer size                     */
138 int     Rawmult;                /* raw/ssd io multiple (from -r)            */
139 int     Minstrides;             /* min # of listio strides per request      */
140 int     Maxstrides;             /* max # of listio strides per request      */
141 int     Oflags;                 /* open(2) flags for creating files         */
142 int     Ocbits;                 /* open(2) cbits for creating files         */
143 int     Ocblks;                 /* open(2) cblks for creating files         */
144 int     Orealtime=0;            /* flag set for -O REALTIME                 */
145 int     Oextsize=0;             /* real-time extent size                    */
146 int     Oreserve=1;             /* flag for -O [no]reserve                  */
147 int     Oallocate=0;            /* flag for -O allocate                     */
148 int     Owrite=1;               /* flag for -O nowrite                      */
149
150 int     Nfiles = 0;             /* # files on cmdline                       */
151 struct  file_info *File_List;   /* info about each file                     */
152 int     Nflags = 0;             /* # flags on cmdline                       */
153 struct  strmap *Flag_List[128]; /* flags selected from cmdline              */
154 int     Nsyscalls = 0;          /* # syscalls on cmdline                    */
155 struct  strmap *Syscall_List[128]; /* syscalls selected on cmdline          */
156 int     Fileio = 0;             /* flag indicating that a file              */
157                                 /* io syscall has been chosen.              */
158 int     Naio_Strat_Types = 0;   /* # async io completion types              */
159 struct  strmap *Aio_Strat_List[128]; /* Async io completion types           */
160
161 void    startup_info();
162
163 /*
164  * Map async io completion modes (-a args) names to values.  Macros are
165  * defined in doio.h.
166  */
167
168 struct strmap   Aio_Strat_Map[] = {
169 #ifndef linux
170         { "poll",       A_POLL          },
171         { "signal",     A_SIGNAL        },
172 #else
173         { "none",       0       },
174 #endif /* !linux */
175 #ifdef CRAY
176 #if _UMK || RELEASE_LEVEL >= 8000
177         { "recall",     A_RECALL        },
178 #endif
179
180 #ifdef RECALL_SIZEOF
181         { "recalla",    A_RECALLA       },
182 #endif
183         { "recalls",    A_RECALLS       },
184 #endif /* CRAY */
185
186 #ifdef sgi
187         { "suspend",    A_SUSPEND       },
188         { "callback",   A_CALLBACK      },
189 #endif
190         { NULL,         -1              }
191 };
192
193 /*
194  * Offset_Mode #defines
195  */
196
197 #define M_RANDOM        1
198 #define M_SEQUENTIAL    2
199 #define M_REVERSE       3
200
201 /*
202  * Map offset mode (-m args) names to values
203  */
204
205 struct strmap   Omode_Map[] = {
206         { "random",             M_RANDOM        },
207         { "sequential",         M_SEQUENTIAL    },
208         { "reverse",            M_REVERSE       },
209         { NULL,                 -1              }
210 };
211
212 /*
213  * Map syscall names (-s args) to values - macros are defined in doio.h.
214  */
215 #define SY_ASYNC        00001
216 #define SY_WRITE        00002
217 #define SY_SDS          00010
218 #define SY_LISTIO       00020
219 #define SY_NENT         00100   /* multi entry vs multi stride >>> */
220
221 struct strmap   Syscall_Map[] = {
222         { "read",               READ,           0                       },
223         { "write",              WRITE,          SY_WRITE                },
224 #ifdef CRAY
225         { "reada",              READA,          SY_ASYNC                },
226         { "writea",             WRITEA,         SY_WRITE|SY_ASYNC       },
227 #ifndef _CRAYMPP
228         { "ssread",             SSREAD,         SY_SDS                  },
229         { "sswrite",            SSWRITE,        SY_WRITE|SY_SDS         },
230 #endif
231         { "listio",             LISTIO,         SY_ASYNC                },
232
233         /* listio as 4 system calls */
234         { "lread",              LREAD,          0                       },
235         { "lreada",             LREADA,         SY_ASYNC                },
236         { "lwrite",             LWRITE,         SY_WRITE                },
237         { "lwritea",            LWRITEA,        SY_WRITE|SY_ASYNC       },
238
239         /* listio with nstrides > 1 */
240         { "lsread",             LSREAD,         0                       },
241         { "lsreada",            LSREADA,        SY_ASYNC                },
242         { "lswrite",            LSWRITE,        SY_WRITE                },
243         { "lswritea",           LSWRITEA,       SY_WRITE|SY_ASYNC       },
244
245         /* listio with nents > 1 */
246         { "leread",             LEREAD,         0|SY_NENT               },
247         { "lereada",            LEREADA,        SY_ASYNC|SY_NENT        },
248         { "lewrite",            LEWRITE,        SY_WRITE|SY_NENT        },
249         { "lewritea",           LEWRITEA,       SY_WRITE|SY_ASYNC|SY_NENT },
250
251         /* listio with nents > 1 & nstrides > 1 */
252
253         /* all listio system calls under one name */
254         { "listio+",            LREAD,          0                       },
255         { "listio+",            LREADA,         SY_ASYNC                },
256         { "listio+",            LWRITE,         SY_WRITE                },
257         { "listio+",            LWRITEA,        SY_WRITE|SY_ASYNC       },
258         { "listio+",            LSREAD,         0                       },
259         { "listio+",            LSREADA,        SY_ASYNC                },
260         { "listio+",            LSWRITE,        SY_WRITE                },
261         { "listio+",            LSWRITEA,       SY_WRITE|SY_ASYNC       },
262         { "listio+",            LEREAD,         0|SY_NENT               },
263         { "listio+",            LEREADA,        SY_ASYNC|SY_NENT        },
264         { "listio+",            LEWRITE,        SY_WRITE|SY_NENT        },
265         { "listio+",            LEWRITEA,       SY_WRITE|SY_ASYNC|SY_NENT },
266 #endif
267
268         { "pread",              PREAD                                   },
269         { "pwrite",             PWRITE,         SY_WRITE                },
270 #ifdef sgi
271         { "aread",              AREAD,          SY_ASYNC                },
272         { "awrite",             AWRITE,         SY_WRITE|SY_ASYNC       },
273 #if 0
274         /* not written yet */
275         { "llread",             LLREAD,         0                       },
276         { "llaread",            LLAREAD,        SY_ASYNC                },
277         { "llwrite",            LLWRITE,        0                       },
278         { "llawrite",           LLAWRITE,       SY_ASYNC                },
279 #endif
280         { "ffsync",             DFFSYNC,        SY_WRITE                },
281 #endif /* SGI */
282 #ifndef NO_XFS
283         { "resvsp",             RESVSP,         SY_WRITE                },
284         { "unresvsp",           UNRESVSP,       SY_WRITE                },
285         { "reserve",            RESVSP,         SY_WRITE                },
286         { "unreserve",          UNRESVSP,       SY_WRITE                },
287 #endif
288
289 #ifndef CRAY
290         { "readv",              READV                                   },
291         { "writev",             WRITEV,         SY_WRITE                },
292         { "mmread",             MMAPR                                   },
293         { "mmwrite",            MMAPW,          SY_WRITE                },
294         { "fsync2",             FSYNC2,         SY_WRITE                },
295         { "fdatasync",          FDATASYNC,      SY_WRITE                },
296 #endif
297
298         { NULL,                 -1      }
299 };
300
301 /*
302  * Map open flags (-f args) to values
303  */
304 #define FLG_RAW         00001
305
306 struct strmap   Flag_Map[] = {
307         { "buffered",           0,                      0       },
308         { "sync",               O_SYNC,                 0       },
309 #ifdef CRAY
310         { "raw",                O_RAW,                  FLG_RAW },
311         { "raw+wf",             O_RAW | O_WELLFORMED,   FLG_RAW },
312         { "raw+wf+ldraw",       O_RAW | O_WELLFORMED | O_LDRAW, FLG_RAW },
313         { "raw+wf+ldraw+sync",  O_RAW | O_WELLFORMED | O_LDRAW | O_SYNC, FLG_RAW },
314 #ifdef O_SSD
315         { "ssd",                O_SSD,                  FLG_RAW },
316 #endif
317 #ifdef O_LDRAW
318         { "ldraw",              O_LDRAW,                0       },
319 #endif
320 #ifdef O_PARALLEL
321         { "parallel",           O_PARALLEL | O_RAW | O_WELLFORMED,
322                   FLG_RAW },
323         { "parallel+sync",      O_PARALLEL | O_RAW | O_WELLFORMED | O_SYNC,
324                   FLG_RAW },
325         { "parallel+ldraw",     O_PARALLEL | O_RAW | O_WELLFORMED | O_LDRAW,
326                   FLG_RAW },
327         { "parallel+ldraw+sync",
328                   O_PARALLEL | O_RAW | O_WELLFORMED | O_LDRAW | O_SYNC,
329                   FLG_RAW },
330 #endif
331 #endif /* CRAY */
332
333         { "direct",             O_DIRECT,               FLG_RAW },
334 #ifdef sgi
335         { "dsync",              O_DSYNC         },      /* affects writes */
336         { "rsync",              O_RSYNC         },      /* affects reads */
337         { "rsync+dsync",        O_RSYNC|O_DSYNC },
338 #endif
339         { NULL,             -1          }
340 };
341
342 /*
343  * Map file types to strings
344  */
345
346 struct strmap   Ftype_Map[] = {
347         { "regular",    S_IFREG },
348         { "blk-spec",   S_IFBLK },
349         { "chr-spec",   S_IFCHR },
350         { NULL,         0 }
351 };
352
353 /*
354  * Misc declarations
355  */
356
357 int             Sds_Avail;
358
359 char    Byte_Patterns[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
360                               'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
361                               'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
362                               'Y', 'Z' };
363
364
365 int form_iorequest(struct io_req *);
366 int init_output();
367 int parse_cmdline(int argc, char **argv, char *opts);
368 int help(FILE *stream);
369 int usage(FILE *stream);
370
371
372 int
373 main(argc, argv)
374 int     argc;
375 char    **argv;
376 {
377     int             rseed, outfd, infinite;
378     time_t          start_time;
379     struct io_req   req;
380     
381     umask(0);
382
383 #ifdef CRAY
384     Sds_Avail = sysconf(_SC_CRAY_SDS);
385 #else
386     Sds_Avail = 0;
387 #endif
388
389     TagName[0] = '\0';
390     parse_cmdline(argc, argv, OPTS);
391
392     /*
393      * Initialize output descriptor.  
394      */
395     if (! p_opt) {
396         outfd = 1;
397     } else {
398         outfd = init_output();
399     }
400
401     rseed = getpid();
402     random_range_seed(rseed);       /* initialize random number generator */
403
404     /*
405      * Print out startup information, unless we're running in quiet mode
406      */
407     if (!q_opt)
408         startup_info(stderr, rseed);
409
410     start_time = time(0);
411
412     /*
413      * While iterations (or forever if Iterations == 0) - compute an
414      * io request, and write the structure to the output descriptor
415      */
416
417     infinite = !Iterations;
418
419     while (infinite ||
420            (! Time_Mode && Iterations--) ||
421            (Time_Mode && time(0) - start_time <= Iterations)) {
422
423         memset(&req, 0, sizeof(struct io_req));
424         if (form_iorequest(&req) == -1) {
425             fprintf(stderr, "iogen%s:  form_iorequest() failed\n", TagName);
426             continue;
427         }
428
429         req.r_magic = DOIO_MAGIC;
430         write(outfd, (char *)&req, sizeof(req));
431     }
432
433     exit(0);
434
435 }   /* main */
436
437 void
438 startup_info(FILE *stream, int seed)
439 {
440     char        *value_to_string(), *type;
441     int         i;
442
443     fprintf(stream, "\n");
444     fprintf(stream, "iogen%s starting up with the following:\n", TagName);
445     fprintf(stream, "\n");
446
447     fprintf(stream, "Out-pipe:              %s\n", 
448             p_opt ? Outpipe : "stdout");
449
450     if (Iterations) {
451         fprintf(stream, "Iterations:            %d", Iterations);
452         if (Time_Mode)
453             fprintf(stream, " seconds");
454
455         fprintf(stream, "\n");
456     } else {
457         fprintf(stream, "Iterations:            Infinite\n");
458     }
459
460     fprintf(stream,
461             "Seed:                  %d\n", seed);
462
463     fprintf(stream,
464             "Offset-Mode:           %s\n", Offset_Mode->m_string);
465
466     fprintf(stream, "Overlap Flag:          %s\n",
467             o_opt ? "on" : "off");
468
469     fprintf(stream,
470             "Mintrans:              %-11d (%d blocks)\n",
471             Mintrans, (Mintrans+BSIZE-1)/BSIZE);
472
473     fprintf(stream,
474             "Maxtrans:              %-11d (%d blocks)\n",
475             Maxtrans, (Maxtrans+BSIZE-1)/BSIZE);
476
477     if (! r_opt) 
478         fprintf(stream,
479                 "O_RAW/O_SSD Multiple:  (Determined by device)\n");
480     else
481         fprintf(stream,
482                 "O_RAW/O_SSD Multiple:  %-11d (%d blocks)\n",
483                 Rawmult, (Rawmult+BSIZE-1)/BSIZE);
484
485     fprintf(stream, "Syscalls:              ");
486     for (i = 0; i < Nsyscalls; i++)
487         fprintf(stream,
488                 "%s ", Syscall_List[i]->m_string);
489     fprintf(stream, "\n");
490
491     fprintf(stream, "Aio completion types:  ");
492     for (i = 0; i < Naio_Strat_Types; i++)
493         fprintf(stream,
494                 "%s ", Aio_Strat_List[i]->m_string);
495     fprintf(stream, "\n");
496
497     if (Fileio) {
498         fprintf(stream, "Flags:                 ");
499         for (i = 0; i < Nflags; i++)
500             fprintf(stream,
501                     "%s ", Flag_List[i]->m_string);
502
503         fprintf(stream, "\n");
504         fprintf(stream, "\n");
505         fprintf(stream, "Test Files:  \n");
506         fprintf(stream, "\n");
507         fprintf(stream,
508                 "Path                                          Length    iou   raw iou file\n");
509         fprintf(stream,
510                 "                                              (bytes) (bytes) (bytes) type\n"); 
511         fprintf(stream,
512                 "-----------------------------------------------------------------------------\n");
513
514         for (i = 0; i < Nfiles; i++) {
515             type = value_to_string(Ftype_Map, File_List[i].f_type);
516             fprintf(stream, "%-40s %12d %7d %7d %s\n",
517                     File_List[i].f_path, File_List[i].f_length,
518                     File_List[i].f_iou, File_List[i].f_riou, type);
519         }
520     }
521 }
522
523 /*
524  * Initialize output descriptor.  If going to stdout, its easy,
525  * otherwise, attempt to create a FIFO on path Outpipe.  Exit with an
526  * error code if this cannot be done.
527  */
528 int
529 init_output()
530 {
531     int  outfd;
532     struct stat     sbuf;
533
534     if (stat(Outpipe, &sbuf) == -1) {
535         if (errno == ENOENT) {
536             if (mkfifo(Outpipe, 0666) == -1) {
537                 fprintf(stderr, "iogen%s:  Could not mkfifo %s:  %s\n",
538                         TagName, Outpipe, SYSERR);
539                 exit(2);
540             }
541         } else {
542             fprintf(stderr, "iogen%s:  Could not stat outpipe %s:  %s\n",
543                     TagName, Outpipe, SYSERR);
544             exit(2);
545         }
546     } else {
547         if (! S_ISFIFO(sbuf.st_mode)) {
548             fprintf(stderr,
549                     "iogen%s:  Output file %s exists, but is not a FIFO\n",
550                     TagName, Outpipe);
551             exit(2);
552         }
553     }
554
555     if ((outfd = open(Outpipe, O_RDWR)) == -1) {
556         fprintf(stderr,
557                 "iogen%s:  Couldn't open outpipe %s with flags O_RDWR: %s\n",
558                 TagName, Outpipe, SYSERR);
559         exit(2);
560     }
561
562     return(outfd);
563 }
564
565
566 /*
567  * Main io generation function.  form_iorequest() selects a system call to
568  * do based on cmdline arguments, and proceeds to select parameters for that
569  * system call.
570  *
571  * Returns 0 if req is filled in with a complete doio request, otherwise
572  * returns -1.
573  */
574
575 int
576 form_iorequest(req)
577 struct io_req   *req;
578 {
579     int                 mult, offset = 0, length = 0, slength;
580     int                 minlength, maxlength, laststart, lastend;
581     int                 minoffset, maxoffset;
582     int                 maxstride, nstrides;
583     char                pattern, *errp;
584     struct strmap       *flags, *sc, *aio_strat;
585     struct file_info    *fptr;
586 #ifdef CRAY
587     int                 opcode, cmd;
588 #endif
589
590     /*
591      * Choose system call, flags, and file
592      */
593
594     sc = Syscall_List[random_range(0, Nsyscalls-1, 1, NULL)];
595     req->r_type = sc->m_value;
596
597 #ifdef CRAY
598     if (sc->m_value == LISTIO ) {
599             opcode = random_range(0, 1, 1, NULL) ? LO_READ : LO_WRITE;
600             cmd = random_range(0, 1, 1, NULL) ? LC_START : LC_WAIT;
601     }
602 #endif
603
604     if( sc->m_flags & SY_WRITE )
605             pattern = Byte_Patterns[random_range(0, sizeof(Byte_Patterns) - 1, 1, NULL)];
606     else
607             pattern = 0;
608
609 #if CRAY
610     /*
611      * If sds io, simply choose a length (possibly pattern) and return
612      */
613
614     if (sc->m_flags & SY_SDS ) {
615             req->r_data.ssread.r_nbytes = random_range(Mintrans, Maxtrans, BSIZE, NULL);
616             if (sc->m_flags & SY_WRITE)
617                     req->r_data.sswrite.r_pattern = pattern;
618
619             return 0;
620     }
621 #endif
622
623     /*
624      * otherwise, we're doing file io.  Choose starting offset, length,
625      * open flags, and possibly a pattern (for write/writea).
626      */
627
628     fptr = &File_List[random_range(0, Nfiles-1, 1, NULL)];
629     flags = Flag_List[random_range(0, Nflags-1, 1, NULL)];
630
631     /*
632      * Choose offset/length multiple.  IO going to a device, or regular
633      * IO that is O_RAW or O_SSD must be aligned on the file r_iou.  Otherwise
634      * it must be aligned on the regular iou (normally 1).
635      */
636
637     if ( fptr->f_type == S_IFREG && (flags->m_flags & FLG_RAW) )
638             mult = fptr->f_riou;
639     else
640             mult = fptr->f_iou;
641
642     /*
643      * Choose offset and length.  Both must be a multiple of mult
644      */
645
646     /*
647      * Choose length first - it must be a multiple of mult
648      */
649
650     laststart = fptr->f_lastoffset;
651     lastend = fptr->f_lastoffset + fptr->f_lastlength - 1;
652
653     minlength = (Mintrans > mult) ? Mintrans : mult;
654
655     switch (Offset_Mode->m_value) {
656     case M_SEQUENTIAL:
657         if (o_opt && lastend > laststart)
658             offset = random_range(laststart, lastend, 1, NULL);
659         else
660             offset = lastend + 1;
661         if (offset && (offset%mult))
662             offset += mult - (offset % mult);
663
664         if (minlength > fptr->f_length - offset)
665             offset = 0;
666
667         maxlength = fptr->f_length - offset;
668         if (maxlength > Maxtrans)
669             maxlength = Maxtrans;
670
671         length = random_range(minlength, maxlength, mult, &errp);
672         if (errp != NULL) {
673             fprintf(stderr, "iogen%s:  random_range(%d, %d, %d) failed\n",
674                     TagName, minlength, maxlength, mult);
675             return -1;
676         }
677
678         break;
679
680     case M_REVERSE:
681         maxlength = laststart;
682
683         if (maxlength > Maxtrans)
684             maxlength = Maxtrans;
685
686         if (minlength > maxlength) {
687             laststart = fptr->f_length;
688             lastend = fptr->f_length;
689             maxlength = Maxtrans;
690         }
691
692         length = random_range(minlength, maxlength, mult, &errp);
693         if (errp != NULL) {
694             fprintf(stderr, "iogen%s:  random_range(%d, %d, %d) failed\n",
695                     TagName, minlength, maxlength, mult);
696             return -1;
697         }
698
699         offset = laststart - length;
700
701         if (o_opt && lastend > laststart)
702             offset += random_range(1, lastend - laststart, 1, NULL);
703
704         if (offset &&  (offset%mult))
705             offset -= offset % mult;
706
707         break;
708     
709     case M_RANDOM:
710         length = random_range(Mintrans, Maxtrans, mult, NULL);
711
712         if (o_opt && lastend > laststart) {
713                 minoffset = laststart - length + 1;
714                 if (minoffset < 0) {
715                         minoffset = 0;
716                 }
717
718                 if (lastend + length > fptr->f_length) {
719                         maxoffset = fptr->f_length - length;
720                 } else {
721                         maxoffset = lastend;
722                 }
723         } else {
724             minoffset = 0;
725             maxoffset = fptr->f_length - length;
726         }
727
728         if (minoffset < 0)
729             minoffset = 0;
730
731         offset = random_range(minoffset, maxoffset, mult, &errp);
732         if (errp != NULL) {
733             fprintf(stderr, "iogen%s:  random_range(%d, %d, %d) failed\n",
734                     TagName, minoffset, maxoffset, mult);
735             return -1;
736         }
737     }
738
739     fptr->f_lastoffset = offset;
740     fptr->f_lastlength = length;
741
742     /*
743      * Choose an async io completion strategy if necessary
744      */
745     if( sc->m_flags & SY_ASYNC )
746             aio_strat = Aio_Strat_List[random_range(0, Naio_Strat_Types - 1,
747                                                     1, NULL)];
748     else
749             aio_strat = NULL;
750
751     /*
752      * fill in specific syscall record data
753      */
754     switch (sc->m_value) {
755     case READ:
756     case READA:
757         strcpy(req->r_data.read.r_file, fptr->f_path);
758         req->r_data.read.r_oflags = O_RDONLY | flags->m_value;
759         req->r_data.read.r_offset = offset;
760         req->r_data.read.r_nbytes = length;
761         req->r_data.read.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
762         req->r_data.read.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
763         req->r_data.read.r_nstrides = 1;
764         req->r_data.read.r_nent = 1;
765         break;
766
767     case WRITE:
768     case WRITEA:
769         strcpy(req->r_data.write.r_file, fptr->f_path);
770         req->r_data.write.r_oflags = O_WRONLY | flags->m_value;
771         req->r_data.write.r_offset = offset;
772         req->r_data.write.r_nbytes = length;
773         req->r_data.write.r_pattern = pattern;
774         req->r_data.write.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
775         req->r_data.write.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
776         req->r_data.write.r_nstrides = 1;
777         req->r_data.write.r_nent = 1;
778         break;
779
780     case READV:
781     case AREAD:
782     case PREAD:
783     case WRITEV:
784     case AWRITE:
785     case PWRITE:
786
787     case LREAD:
788     case LREADA:
789     case LWRITE:
790     case LWRITEA:
791
792     case RESVSP:
793     case UNRESVSP:
794     case FSYNC2:
795     case FDATASYNC:
796
797         strcpy(req->r_data.io.r_file, fptr->f_path);
798         req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_WRONLY : O_RDONLY) | flags->m_value;
799         req->r_data.io.r_offset = offset;
800         req->r_data.io.r_nbytes = length;
801         req->r_data.io.r_pattern = pattern;
802         req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
803         req->r_data.io.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
804         req->r_data.io.r_nstrides = 1;
805         req->r_data.io.r_nent = 1;
806         break;
807
808     case MMAPR:
809     case MMAPW:
810         strcpy(req->r_data.io.r_file, fptr->f_path);
811         /* a subtle "feature" of mmap: a write-map requires
812            the file open read/write */
813         req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_RDWR : O_RDONLY) | flags->m_value;
814         req->r_data.io.r_offset = offset;
815         req->r_data.io.r_nbytes = length;
816         req->r_data.io.r_pattern = pattern;
817         req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
818         req->r_data.io.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
819         req->r_data.io.r_nstrides = 1;
820         req->r_data.io.r_nent = 1;
821         break;
822
823     case LSREAD:
824     case LSREADA:
825     case LEREAD:
826     case LEREADA:
827     case LSWRITE:
828     case LSWRITEA:
829     case LEWRITE:
830     case LEWRITEA:
831         /* multi-strided */
832         strcpy(req->r_data.io.r_file, fptr->f_path);
833         req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_WRONLY : O_RDONLY) | flags->m_value;
834         req->r_data.io.r_offset = offset;
835         req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
836         req->r_data.io.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
837         req->r_data.io.r_pattern = pattern;
838
839         /* multi-strided request...
840          *  random number of strides (1...MaxStrides)
841          *  length of stride must be > minlength
842          *  length of stride must be % mult
843          *
844          * maxstrides = min(length / mult, overall.max#strides)
845          * nstrides = random #
846          * while( length / nstrides < minlength )
847          *      nstrides = new random #
848          */
849         maxstride = length / mult;
850         if(maxstride > Maxstrides)
851             maxstride = Maxstrides;
852
853         if(!Minstrides)
854                 Minstrides=1;
855         nstrides = random_range(Minstrides, maxstride, 1, &errp);
856         if (errp != NULL) {
857             fprintf(stderr, "iogen%s:  random_range(%d, %d, %d) failed\n",
858                     TagName, Minstrides, maxstride, 1);
859             return -1;
860         }
861
862         slength = length / nstrides;
863         if(slength % mult != 0) {
864             if( mult > slength) {
865                 slength = mult;
866             } else {
867                 slength -= slength % mult;
868             }
869             nstrides = length / slength;
870             if(nstrides > Maxstrides)
871                     nstrides = Maxstrides;
872         }
873
874         req->r_data.io.r_nbytes = slength;
875         if( sc->m_flags & SY_NENT ) {
876                 req->r_data.io.r_nstrides = 1;
877                 req->r_data.io.r_nent = nstrides;
878         } else {
879                 req->r_data.io.r_nstrides = nstrides;
880                 req->r_data.io.r_nent = 1;
881         }
882         break;
883
884     case LISTIO:
885 #ifdef CRAY
886         strcpy(req->r_data.listio.r_file, fptr->f_path);
887         req->r_data.listio.r_offset = offset;
888         req->r_data.listio.r_cmd = cmd;
889         req->r_data.listio.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
890         req->r_data.listio.r_filestride = 0;
891         req->r_data.listio.r_memstride = 0;
892         req->r_data.listio.r_opcode = opcode;
893         req->r_data.listio.r_nstrides = 1;
894         req->r_data.listio.r_nbytes = length;
895         req->r_data.listio.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
896
897         if (opcode == LO_WRITE) {
898                 req->r_data.listio.r_pattern = pattern;
899                 req->r_data.listio.r_oflags = O_WRONLY | flags->m_value;
900         } else {
901                 req->r_data.listio.r_oflags = O_RDONLY | flags->m_value;
902         }
903 #endif
904         break;
905     }
906
907     return 0;
908 }
909
910 /*
911  * Get information about a file that iogen uses to choose io length and
912  * offset.  Information gathered is file length, iounit, and raw iounit.
913  * For regurlar files, iounit is 1, and raw iounit is the iounit of the
914  * device on which the file resides.  For block/character special files
915  * the iounit and raw iounit are both the iounit of the device.
916  *
917  * Note:        buffered and osync io must be iounit aligned
918  *              raw and ossd io must be raw iounit aligned
919  */
920
921 int
922 get_file_info(rec)
923 struct file_info    *rec;
924 {
925     struct stat                 sbuf;
926 #ifdef CRAY
927     struct lk_device_info       dinfo;
928 #endif
929 #ifndef NO_XFS
930     int                         fd;
931     struct dioattr              finfo;
932 #endif
933
934     /*
935      * Figure out if the files is regular, block or character special.  Any
936      * other type is an error.
937      */
938
939     if (stat(rec->f_path, &sbuf) == -1) {
940         fprintf(stderr, "iogen%s: get_file_info():  Could not stat() %s:  %s\n",
941                 TagName, rec->f_path, SYSERR);
942         return -1;
943     }
944
945 #if _CRAY2
946     if ((! S_ISREG(sbuf.st_mode)) || strncmp(rec->f_path, "/dev/", 5) == 0) {
947         fprintf(stderr, "iogen%s:  device level io not supported on cray2\n", TagName);
948         return -1;
949     }
950 #endif
951
952     rec->f_type = sbuf.st_mode & S_IFMT;
953
954     /*
955      * If regular, iou is 1, and we must figure out the device on
956      * which the file resides.  riou is the iou (logical sector size) of
957      * this device.
958      */
959
960     if (S_ISREG(sbuf.st_mode)) {
961         rec->f_iou = 1;
962         rec->f_length = sbuf.st_size;
963
964         /*
965          * If -r used, take Rawmult as the raw/ssd io multiple.  Otherwise
966          * attempt to determine it by looking at the device the file
967          * resides on.
968          */
969
970         if (r_opt) {
971             rec->f_riou = Rawmult;
972             return 0;
973         }
974
975         rec->f_riou = BSIZE;
976 #ifdef CRAY
977         if (lk_rawdev(rec->f_path, dinfo.path, sizeof(dinfo.path), 0) == -1)
978             return -1;
979
980         if (lk_devinfo(&dinfo, 0) == -1) {
981             /* can't get raw I/O unit -- use stat to fudge it */
982             rec->f_riou = sbuf.st_blksize;
983         } else {
984             rec->f_riou = ctob(dinfo.iou);
985         }
986 #endif
987 #ifndef NO_XFS
988         if( (fd = open(rec->f_path, O_RDWR|O_DIRECT, 0)) != -1 ) {
989             if(xfsctl(rec->f_path, fd, XFS_IOC_DIOINFO, &finfo) != -1) {
990                 rec->f_riou = finfo.d_miniosz;
991             } else {
992                 fprintf(stderr,
993                         "iogen%s: Error %s (%d) getting direct I/O info of file %s\n",
994                         TagName, strerror(errno), errno, rec->f_path);
995             }
996             close(fd);
997         } else {
998             rec->f_riou = BBSIZE;
999         }
1000 #endif
1001
1002     } else {
1003
1004 #ifdef CRAY
1005         /*
1006          * Otherwise, file is a device.  Use lk_devinfo() to get its logical
1007          * sector size.  This is the iou and riou
1008          */
1009
1010         strcpy(dinfo.path, rec->f_path);
1011
1012         if (lk_devinfo(&dinfo, 0) == -1) {
1013             fprintf(stderr, "iogen%s: %s:  %s\n", TagName, Lk_err_func, Lk_err_mesg);
1014             return -1;
1015         }
1016
1017         rec->f_iou = ctob(dinfo.iou);
1018         rec->f_riou = ctob(dinfo.iou);
1019         rec->f_length = ctob(dinfo.length);
1020 #else
1021 #ifdef sgi
1022         rec->f_riou = BBSIZE;
1023         rec->f_length = BBSIZE;
1024 #else
1025         rec->f_riou = BSIZE;
1026         rec->f_length = BSIZE;
1027 #endif /* sgi */
1028 #endif /* CRAY */
1029     }
1030
1031     return 0;
1032 }
1033
1034 /*
1035  * Create file path as nbytes long.  If path exists, the file will either be
1036  * extended or truncated to be nbytes long.  Returns final size of file,
1037  * or -1 if there was a failure.
1038  */
1039
1040 int
1041 create_file(path, nbytes)
1042 char    *path;
1043 int     nbytes;
1044 {
1045     int         fd, rval;
1046     char        c;
1047     struct stat sbuf;
1048 #ifndef NO_XFS
1049     int         nb;
1050     struct xfs_flock64 f;
1051     struct fsxattr xattr;
1052     struct dioattr finfo;
1053     char        *b, *buf;
1054 #endif
1055
1056     errno = 0;
1057     rval = stat(path, &sbuf);
1058
1059     if (rval == -1) {
1060         if (errno == ENOENT) {
1061             sbuf.st_size = 0;
1062         } else {
1063             fprintf(stderr, "iogen%s:  Could not stat file %s:  %s (%d)\n",
1064                     TagName, path, SYSERR, errno);
1065             return -1;
1066         }
1067     } else {
1068         if (! S_ISREG(sbuf.st_mode)) {
1069             fprintf(stderr,
1070                     "iogen%s:  file %s exists, but is not a regular file - cannot modify length\n",
1071                     TagName, path);
1072             return -1;
1073         }
1074     }
1075
1076     if (sbuf.st_size == nbytes)
1077         return nbytes;
1078
1079     Oflags |= O_CREAT | O_WRONLY;
1080
1081     if ((fd = open(path, Oflags, 0666, Ocbits, Ocblks)) == -1) {
1082         fprintf(stderr, "iogen%s:  Could not create/open file %s: %s (%d)\n",
1083                 TagName, path, SYSERR, errno);
1084         return -1;
1085     }
1086
1087     /*
1088      * Truncate file if it is longer than nbytes, otherwise attempt to
1089      * pre-allocate file blocks.
1090      */
1091
1092     if (sbuf.st_size > nbytes) {
1093         if (ftruncate(fd, nbytes) == -1) {
1094             fprintf(stderr,
1095                     "iogen%s:  Could not ftruncate() %s to %d bytes:  %s (%d)\n",
1096                     TagName, path, nbytes, SYSERR, errno);
1097             close(fd);
1098             return -1;
1099         }
1100     } else {
1101
1102 #ifndef NO_XFS
1103         /*
1104          *  The file must be designated as Real-Time before any data
1105          *  is allocated to it.
1106          *
1107          */
1108         if(Orealtime != 0) {
1109             bzero(&xattr, sizeof(xattr));
1110             xattr.fsx_xflags = XFS_XFLAG_REALTIME;
1111             /*fprintf(stderr, "set: fsx_xflags = 0x%x\n", xattr.fsx_xflags);*/
1112             if( xfsctl(path, fd, XFS_IOC_FSSETXATTR, &xattr) == -1 ) {
1113                 fprintf(stderr, "iogen%s: Error %s (%d) setting XFS XATTR->Realtime on file %s\n",
1114                         TagName, SYSERR, errno, path);
1115                 close(fd);
1116                 return -1;
1117             }
1118
1119 #ifdef DEBUG
1120             if( xfsctl(path, fd, XFS_IOC_FSGETXATTR, &xattr) == -1 ) {
1121                 fprintf(stderr, "iogen%s: Error getting realtime flag %s (%d)\n",
1122                         TagName, SYSERR, errno);
1123                 close(fd);
1124                 return -1;
1125             } else {
1126                 fprintf(stderr, "get: fsx_xflags = 0x%x\n", 
1127                         xattr.fsx_xflags);
1128             }
1129 #endif
1130         }
1131
1132         /*
1133          * Reserve space
1134          *
1135          * Failure is ignored since XFS_IOC_RESVSP only works on XFS and the
1136          * filesystem could be on some otehr filesystem.
1137          */
1138         if( Oreserve ) {
1139             f.l_whence = SEEK_SET;
1140             f.l_start = 0;
1141             f.l_len = nbytes;
1142             
1143             /*fprintf(stderr,
1144                     "create_file: xfsctl(%d, RESVSP, { %d, %lld, %lld })\n",
1145                    fd, f.l_whence, (long long)f.l_start, (long long)f.l_len);*/
1146
1147             /* non-zeroing reservation */
1148             if( xfsctl( path, fd, XFS_IOC_RESVSP, &f ) == -1) {
1149                 fprintf(stderr,
1150                         "iogen%s:  Could not xfsctl(XFS_IOC_RESVSP) %d bytes in file %s: %s (%d)\n",
1151                         TagName, nbytes, path, SYSERR, errno);
1152                 close(fd);
1153                 return -1;
1154             }
1155         }
1156
1157         if( Oallocate ) {
1158             /* XFS_IOC_ALLOCSP allocates from the file start to l_start */
1159             f.l_whence = SEEK_SET;
1160             f.l_start = nbytes;
1161             f.l_len = 0;
1162             /*fprintf(stderr,
1163                     "create_file: xfsctl(%d, XFS_IOC_ALLOCSP, { %d, %lld, %lld })\n",
1164                     fd, f.l_whence, (long long)f.l_start,
1165                     (long long)f.l_len);*/
1166
1167             /* zeroing reservation */
1168             if( xfsctl( path, fd, XFS_IOC_ALLOCSP, &f ) == -1) {
1169                 fprintf(stderr,
1170                         "iogen%s:  Could not xfsctl(XFS_IOC_ALLOCSP) %d bytes in file %s: %s (%d)\n",
1171                         TagName, nbytes, path, SYSERR, errno);
1172                 close(fd);
1173                 return -1;
1174             }
1175         }
1176 #endif
1177
1178         /*
1179          * Write a byte at the end of file so that stat() sets the right
1180          * file size.
1181          */
1182
1183 #ifndef NO_XFS
1184         if(Owrite == 2) {
1185             close(fd);
1186             if( (fd = open(path, O_CREAT|O_RDWR|O_DIRECT, 0)) != -1 ) {
1187                 if(xfsctl(path, fd, XFS_IOC_DIOINFO, &finfo) == -1) {
1188                     fprintf(stderr,
1189                             "iogen%s: Error %s (%d) getting direct I/O info for file %s\n",
1190                             TagName, SYSERR, errno, path);
1191                     return -1;
1192                 } else {
1193                     /*fprintf(stderr, "%s: miniosz=%d\n", 
1194                             path, finfo.d_miniosz);*/
1195                 }
1196             } else {
1197                 fprintf(stderr, "iogen%s: Error %s (%d) opening file %s with flags O_CREAT|O_RDWR|O_DIRECT\n",
1198                         TagName, SYSERR, errno, path);
1199                 return -1;
1200             }
1201
1202             /*
1203              * nb is nbytes adjusted down by an even d_miniosz block
1204              *
1205              * Note: the first adjustment can cause iogen to print a warning 
1206              *  about not being able to create a file of <nbytes> length, 
1207              *  since the file will be shorter.
1208              */
1209             nb = nbytes-finfo.d_miniosz;
1210             nb = nb-nb%finfo.d_miniosz;
1211
1212             /*fprintf(stderr,
1213                     "create_file_ow2: lseek(%d, %d {%d %d}, SEEK_SET)\n",
1214                     fd, nb, nbytes, finfo.d_miniosz);*/
1215
1216             if (lseek(fd, nb, SEEK_SET) == -1) {
1217                 fprintf(stderr,
1218                         "iogen%s:  Could not lseek() to EOF of file %s: %s (%d)\n\tactual offset %d file size goal %d miniosz %lld\n",
1219                         TagName, path, SYSERR, errno,
1220                         nb, nbytes, (long long)finfo.d_miniosz);
1221                 close(fd);
1222                 return -1;
1223             }
1224
1225             b = buf = (char *)malloc(finfo.d_miniosz+finfo.d_mem);
1226
1227             if( ((long)buf % finfo.d_mem != 0) ) {
1228                 buf += finfo.d_mem - ((long)buf % finfo.d_mem);
1229             }
1230             
1231             memset(buf, 0, finfo.d_miniosz);
1232
1233             if ( (rval=write(fd, buf, finfo.d_miniosz)) != finfo.d_miniosz) {
1234                 fprintf(stderr,
1235                         "iogen%s:  Could not write %d byte length file %s: %s (%d)\n",
1236                         TagName, nb, path, SYSERR, errno);
1237                 fprintf(stderr,
1238                         "\twrite(%d, 0x%lx, %d) = %d\n",
1239                         fd, (long)buf, finfo.d_miniosz, rval);
1240                 fprintf(stderr,
1241                         "\toffset %d file size goal %d, miniosz=%d\n",
1242                         nb, nbytes, finfo.d_miniosz);
1243                 close(fd);
1244                 return -1;
1245             }
1246             free(b);
1247         } else
1248 #endif
1249             if(Owrite) {
1250             /*fprintf(stderr,
1251                     "create_file_Owrite: lseek(%d, %d {%d}, SEEK_SET)\n",
1252                     fd, nbytes-1, nbytes);*/
1253
1254             if (lseek(fd, nbytes-1, SEEK_SET) == -1) {
1255                 fprintf(stderr,
1256                         "iogen%s:  Could not lseek() to EOF in file %s:  %s (%d)\n\toffset goal %d\n",
1257                         TagName, path, SYSERR, errno,
1258                         nbytes-1);
1259                 close(fd);
1260                 return -1;
1261             }
1262
1263             if ( (rval=write(fd, &c, 1)) != 1) {
1264                 fprintf(stderr,
1265                         "iogen%s:  Could not create a %d byte length file %s: %s (%d)\n",
1266                         TagName, nbytes, path, SYSERR, errno);
1267                 fprintf(stderr,
1268                         "\twrite(%d, 0x%lx, %d) = %d\n",
1269                         fd, (long)&c, 1, rval);
1270                 fprintf(stderr,
1271                         "\toffset %d file size goal %d\n",
1272                         nbytes-1, nbytes);
1273                 close(fd);
1274                 return -1;
1275             }
1276         }
1277     }
1278
1279     fstat(fd, &sbuf);
1280     close(fd);
1281
1282     return sbuf.st_size;
1283 }
1284
1285 /*
1286  * Function to convert a string to its corresponding value in a strmap array.
1287  * If the string is not found in the array, the value corresponding to the
1288  * NULL string (the last element in the array) is returned.
1289  */
1290
1291 int
1292 str_to_value(map, str)
1293 struct strmap   *map;
1294 char            *str;
1295 {
1296     struct strmap   *mp;
1297
1298     for (mp = map; mp->m_string != NULL; mp++)
1299         if (strcmp(mp->m_string, str) == 0)
1300             break;
1301
1302     return mp->m_value;
1303 }
1304     
1305 /*
1306  * Function to convert a string to its corresponding entry in a strmap array.
1307  * If the string is not found in the array, a NULL is returned.
1308  */
1309
1310 struct strmap *
1311 str_lookup(map, str)
1312 struct strmap   *map;
1313 char            *str;
1314 {
1315     struct strmap   *mp;
1316
1317     for (mp = map; mp->m_string != NULL; mp++)
1318         if (strcmp(mp->m_string, str) == 0)
1319             break;
1320
1321     return((mp->m_string == NULL) ? NULL : mp);
1322 }
1323     
1324
1325 /*
1326  * Function to convert a value to its corresponding string in a strmap array.
1327  * If the value is not found in the array, NULL is returned.
1328  */
1329
1330 char *
1331 value_to_string(map, val)
1332 struct strmap   *map;
1333 int             val;
1334 {
1335     struct strmap  *mp;
1336
1337     for (mp = map; mp->m_string != NULL; mp++)
1338         if (mp->m_value == val)
1339             break;
1340
1341     return mp->m_string;
1342 }
1343
1344 /*
1345  * Interpret cmdline options/arguments.  Exit with 1 if something on the
1346  * cmdline isn't kosher.
1347  */
1348
1349 int
1350 parse_cmdline(argc, argv, opts)
1351 int     argc;
1352 char    **argv;
1353 char    *opts;
1354 {
1355     int                 o, len, nb, format_error;
1356     struct strmap       *flgs, *sc;
1357     char                *file, *cp, ch;
1358     struct strmap       *mp;
1359     struct file_info    *fptr;
1360     int                 nopenargs;
1361     char                *openargs[5];   /* Flags, cbits, cblks */
1362 #ifdef CRAY
1363     char                *errmsg;
1364     char                *ranges;
1365     struct strmap       *type;
1366 #endif
1367
1368     while ((o = getopt(argc, argv, opts)) != EOF) {
1369         switch ((char)o) {
1370
1371         case 'a':
1372 #ifndef CRAY
1373             fprintf(stderr, "iogen%s:  Unrecognized option -a on this platform\n", TagName);
1374             exit(2);
1375 #else
1376             cp = strtok(optarg, ",");
1377             while (cp != NULL) {
1378                 if ((type = str_lookup(Aio_Strat_Map, cp)) == NULL) {
1379                     fprintf(stderr, "iogen%s:  Unrecognized aio completion strategy:  %s\n", TagName, cp);
1380                     exit(2);
1381                 }
1382
1383                 Aio_Strat_List[Naio_Strat_Types++] = type;
1384                 cp = strtok(NULL, ",");
1385             }
1386             a_opt++;
1387 #endif
1388             break;
1389
1390         case 'f':
1391             cp = strtok(optarg, ",");
1392             while (cp != NULL) {
1393                 if( (flgs = str_lookup(Flag_Map, cp)) == NULL ) {
1394                     fprintf(stderr, "iogen%s:  Unrecognized flags:  %s\n", TagName, cp);
1395                     exit(2);
1396                 }
1397
1398                 cp = strtok(NULL, ",");
1399
1400 #ifdef O_SSD
1401                 if (flgs->m_value & O_SSD && ! Sds_Avail) {
1402                     fprintf(stderr, "iogen%s:  Warning - no sds available, ignoring ssd flag\n", TagName);
1403                     continue;
1404                 }
1405 #endif
1406
1407                 Flag_List[Nflags++] = flgs;
1408             }
1409             f_opt++;
1410             break;
1411
1412         case 'h':
1413             help(stdout);
1414             exit(0);
1415             break;
1416
1417         case 'i':
1418             format_error = 0;
1419
1420             switch (sscanf(optarg, "%i%c", &Iterations, &ch)) {
1421             case 1:
1422                 Time_Mode = 0;
1423                 break;
1424
1425             case 2:
1426                 if (ch == 's')
1427                     Time_Mode = 1;
1428                 else
1429                     format_error = 1;
1430                 break;
1431
1432             default:
1433                 format_error = 1;
1434             }
1435
1436             if (Iterations < 0)
1437                 format_error = 1;
1438
1439             if (format_error) {
1440                 fprintf(stderr, "iogen%s:  Illegal -i arg (%s):  Must be of the format:  number[s]\n", TagName, optarg);
1441                 fprintf(stderr, "        where 'number' is >= 0\n");
1442                 exit(1);
1443             }
1444
1445             i_opt++;
1446             break;
1447
1448         case 'L':
1449 #ifndef CRAY
1450             fprintf(stderr, "iogen%s:  Unrecognized option -L on this platform\n", TagName);
1451             exit(2);
1452 #else
1453             if( parse_ranges(optarg, 1, 255, 1, NULL, &ranges, 
1454                              &errmsg ) == -1 ) {
1455                     fprintf(stderr, "iogen%s: error parsing listio range '%s': %s\n",
1456                             TagName, optarg, errmsg);
1457                     exit(1);
1458             }
1459
1460             Minstrides = range_min(ranges, 0);
1461             Maxstrides = range_max(ranges, 0);
1462
1463             free(ranges);
1464             L_opt++;
1465 #endif
1466             break;
1467
1468         case 'm':
1469             if ((Offset_Mode = str_lookup(Omode_Map, optarg)) == NULL) {
1470                 fprintf(stderr, "iogen%s:  Illegal -m arg (%s)\n", TagName, optarg);
1471                 exit(1);
1472             }
1473
1474             m_opt++;
1475             break;
1476
1477         case 'N':
1478             sprintf( TagName, "(%.39s)", optarg );
1479             break;
1480
1481         case 'o':
1482             o_opt++;
1483             break;
1484
1485         case 'O':
1486
1487             nopenargs = string_to_tokens(optarg, openargs, 4, ":/");
1488
1489 #ifdef CRAY
1490             if(nopenargs)
1491                 sscanf(openargs[1],"%i", &Ocbits);
1492             if(nopenargs > 1)
1493                 sscanf(openargs[2],"%i", &Ocblks);
1494
1495             Oflags = parse_open_flags(openargs[0], &errmsg);
1496             if(Oflags == -1) {
1497                 fprintf(stderr, "iogen%s: -O %s error: %s\n", TagName, optarg, errmsg);
1498                 exit(1);
1499             }
1500 #endif
1501 #ifndef NO_XFS
1502             if(!strcmp(openargs[0], "realtime")) {
1503                 /*
1504                  * -O realtime:extsize
1505                  */
1506                 Orealtime = 1;
1507                 if(nopenargs > 1)
1508                     sscanf(openargs[1],"%i", &Oextsize);
1509                 else
1510                     Oextsize=0;
1511             } else if( !strcmp(openargs[0], "allocate") ||
1512                        !strcmp(openargs[0], "allocsp")) {
1513                 /*
1514                  * -O allocate
1515                  */
1516                 Oreserve=0;
1517                 Oallocate=1;
1518             } else if(!strcmp(openargs[0], "reserve")) {
1519                 /*
1520                  * -O [no]reserve
1521                  */
1522                 Oallocate=0;
1523                 Oreserve=1;
1524             } else if(!strcmp(openargs[0], "noreserve")) {
1525                 /* Oreserve=1 by default; this clears that default */
1526                 Oreserve=0;
1527             } else if(!strcmp(openargs[0], "nowrite")) {
1528                 /* Owrite=1 by default; this clears that default */
1529                 Owrite=0;
1530             } else if(!strcmp(openargs[0], "direct")) {
1531                 /* this means "use direct i/o to preallocate file" */
1532                 Owrite=2;
1533             } else {
1534                 fprintf(stderr, "iogen%s: Error: -O %s error: unrecognized option\n",
1535                         TagName, openargs[0]);
1536                 exit(1);
1537             }
1538 #else
1539             Oflags = parse_open_flags(openargs[0], &errmsg);
1540             if(Oflags == -1) {
1541                 fprintf(stderr, "iogen%s: -O %s error: %s\n", TagName, optarg, errmsg);
1542                 exit(1);
1543             }
1544 #endif
1545
1546             O_opt++;
1547             break;
1548
1549         case 'p':
1550             Outpipe = optarg;
1551             p_opt++;
1552             break;
1553
1554         case 'r':
1555             if ((Rawmult = str_to_bytes(optarg)) == -1 ||
1556                           Rawmult < 11 || Rawmult % BSIZE) {
1557                 fprintf(stderr, "iogen%s:  Illegal -r arg (%s).  Must be > 0 and multipe of BSIZE (%d)\n",
1558                         TagName, optarg, BSIZE);
1559                 exit(1);
1560             }
1561
1562             r_opt++;
1563             break;
1564
1565         case 's':
1566             cp = strtok(optarg, ",");
1567             while (cp != NULL) {
1568                 if ((sc = str_lookup(Syscall_Map, cp)) == NULL) {
1569                     fprintf(stderr, "iogen%s:  Unrecognized syscall:  %s\n", TagName, cp);
1570                     exit(2);
1571                 }
1572
1573                 do {
1574                     /* >>> sc->m_flags & FLG_SDS */
1575                     if (sc->m_value != SSREAD && sc->m_value != SSWRITE)
1576                         Fileio++;
1577
1578                     Syscall_List[Nsyscalls++] = sc;
1579                 } while ( (sc = str_lookup(++sc, cp)) != NULL);
1580
1581                 cp = strtok(NULL, ",");
1582             }
1583             s_opt++;
1584             break;
1585
1586         case 't':
1587             if ((Mintrans = str_to_bytes(optarg)) == -1) {
1588                 fprintf(stderr, "iogen%s:  Illegal -t arg (%s):  Must have the form num[bkm]\n", TagName, optarg);
1589                 exit(1);
1590             }
1591             t_opt++;
1592             break;
1593
1594         case 'T':
1595             if ((Maxtrans = str_to_bytes(optarg)) == -1) {
1596                 fprintf(stderr, "iogen%s:  Illegal -T arg (%s):  Must have the form num[bkm]\n", TagName, optarg);
1597                 exit(1);
1598             }
1599             T_opt++;
1600             break;
1601
1602         case 'q':
1603             q_opt++;
1604             break;
1605
1606         case '?':
1607             usage(stderr);
1608             exit(1);
1609         }
1610     }
1611
1612     /*
1613      * Supply defaults
1614      */
1615
1616     if( ! L_opt ) {
1617             Minstrides = 1;
1618             Maxstrides = 255;
1619     }
1620
1621     if (! m_opt)
1622         Offset_Mode = str_lookup(Omode_Map, "sequential");
1623
1624     if (! i_opt)
1625         Iterations = 0;
1626
1627     if (! t_opt)
1628         Mintrans = 1;
1629
1630     if (! T_opt)
1631         Maxtrans = 256 * BSIZE;
1632
1633     if( ! O_opt) 
1634         Oflags = Ocbits = Ocblks = 0;
1635
1636     /*
1637      * Supply default async io completion strategy types.
1638      */
1639
1640     if (! a_opt) {
1641             for (mp = Aio_Strat_Map; mp->m_string != NULL; mp++) {
1642                     Aio_Strat_List[Naio_Strat_Types++] = mp;
1643             }
1644     }
1645
1646     /*
1647      * Supply default syscalls.  Default is read,write,reada,writea,listio.
1648      */
1649
1650     if (! s_opt) {
1651         Nsyscalls = 0;
1652         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "read");
1653         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "write");
1654 #ifdef CRAY
1655         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "reada");
1656         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "writea");
1657         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lread");
1658         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lreada");
1659         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lwrite");
1660         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lwritea");
1661 #endif
1662
1663         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "pread");
1664         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "pwrite");
1665 #ifdef sgi
1666         /*Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "aread");*/
1667         /*Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "awrite");*/
1668 #endif
1669
1670 #ifndef CRAY
1671         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "readv");
1672         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "writev");
1673         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "mmread");
1674         Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "mmwrite");
1675 #endif
1676
1677         Fileio = 1;
1678     }
1679
1680     if (Fileio && (argc - optind < 1)) {
1681         fprintf(stderr, "iogen%s:  No files specified on the cmdline\n", TagName);
1682         exit(1);
1683     }
1684
1685     /*
1686      * Supply default file io flags - defaut is 'buffered,raw,sync,ldraw'.
1687      */
1688
1689     if (! f_opt && Fileio) {
1690             Nflags = 0;
1691             Flag_List[Nflags++] = str_lookup(Flag_Map, "buffered");
1692             Flag_List[Nflags++] = str_lookup(Flag_Map, "sync");
1693 #ifdef CRAY
1694             Flag_List[Nflags++] = str_lookup(Flag_Map, "raw+wf");
1695             Flag_List[Nflags++] = str_lookup(Flag_Map, "ldraw");
1696 #endif
1697
1698 #ifdef sgi
1699             /* Warning: cannot mix direct i/o with others! */
1700             Flag_List[Nflags++] = str_lookup(Flag_Map, "dsync");
1701             Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync");
1702             /* Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync+sync");*/
1703             /* Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync+dsync");*/
1704 #endif
1705     }
1706
1707     if (Fileio) {
1708         if (optind >= argc) {
1709             fprintf(stderr, "iogen%s:  No files listed on the cmdline\n", TagName);
1710             exit(1);
1711         }
1712
1713         /*
1714          * Initialize File_List[] - only necessary if doing file io.  First 
1715          * space for the File_List array, then fill it in.
1716          */
1717         
1718         File_List = (struct file_info *)
1719             malloc((argc-optind) * sizeof(struct file_info));
1720         
1721         if (File_List == NULL) {
1722             fprintf(stderr, "iogen%s:  Could not malloc space for %d file_info structures\n", TagName, argc-optind);
1723             exit(2);
1724         }
1725
1726         memset(File_List, 0, (argc-optind) * sizeof(struct file_info));
1727
1728         Nfiles = 0;
1729         while (optind < argc) {
1730             len = -1;
1731
1732             /*
1733              * Pick off leading len: if it's there and create/extend/trunc
1734              * the file to the desired length.  Otherwise, just make sure
1735              * the file is accessable.
1736              */
1737
1738             if ((cp = strchr(argv[optind], ':')) != NULL) {
1739                 *cp = '\0';
1740                 if ((len = str_to_bytes(argv[optind])) == -1) {
1741                     fprintf(stderr,
1742                             "iogen%s:  illegal file length (%s) for file %s\n",
1743                             TagName, argv[optind], cp+1);
1744                     exit(2);
1745                 }
1746                 *cp = ':';
1747                 file = cp+1;
1748
1749                 if (strlen(file) > MAX_FNAME_LENGTH) {
1750                     fprintf(stderr, "iogen%s:  Max fname length is %d chars - ignoring file %s\n",
1751                             TagName, MAX_FNAME_LENGTH, file);
1752                     optind++;
1753                     continue;
1754                 }
1755
1756                 nb = create_file(file, len);
1757
1758                 if (nb < len) {
1759                     fprintf(stderr,
1760                             "iogen%s warning:  Couldn't create file %s of %d bytes\n",
1761                             TagName, file, len);
1762
1763                     if (nb <= 0) {
1764                         optind++;
1765                         continue;
1766                     }
1767                 }
1768             } else {
1769                 file = argv[optind];
1770                 if (access(file, R_OK | W_OK) == -1) {
1771                     fprintf(stderr, "iogen%s:  file %s cannot be accessed for reading and/or writing:  %s (%d)\n",
1772                             TagName, file, SYSERR, errno);
1773                     exit(2);
1774                 }
1775             }
1776
1777             /*
1778              * get per-file information
1779              */
1780
1781             fptr = &File_List[Nfiles];
1782
1783             if (file[0] == '/') {
1784                 strcpy(fptr->f_path, file);
1785             } else {
1786                 getcwd(fptr->f_path,
1787                        sizeof(fptr->f_path)-1);
1788                 strcat(fptr->f_path, "/");
1789                 strcat(fptr->f_path, file);
1790             }
1791
1792             if (get_file_info(fptr) == -1) {
1793                 fprintf(stderr, "iogen%s warning:  Error getting file info for %s\n", TagName, file);
1794             } else {
1795
1796                 /*
1797                  * If the file length is smaller than our min transfer size,
1798                  * ignore it.
1799                  */
1800
1801                 if (fptr->f_length < Mintrans) {
1802                     fprintf(stderr, "iogen%s warning:  Ignoring file %s\n",
1803                             TagName, fptr->f_path);
1804                     fprintf(stderr, "                length (%d) is < min transfer size (%d)\n",
1805                             fptr->f_length, Mintrans);
1806                     optind++;
1807                     continue;
1808                 }
1809
1810                 /*
1811                  * If the file length is smaller than our max transfer size,
1812                  * ignore it.
1813                  */
1814
1815                 if (fptr->f_length < Maxtrans) {
1816                     fprintf(stderr, "iogen%s warning:  Ignoring file %s\n",
1817                             TagName, fptr->f_path);
1818                     fprintf(stderr, "                length (%d) is < max transfer size (%d)\n",
1819                             fptr->f_length, Maxtrans);
1820                     optind++;
1821                     continue;
1822                 }
1823
1824                 if (fptr->f_length > 0) {
1825                     switch (Offset_Mode->m_value) {
1826                     case M_SEQUENTIAL:
1827                         fptr->f_lastoffset = 0;
1828                         fptr->f_lastlength = 0;
1829                         break;
1830                         
1831                     case M_REVERSE:
1832                         fptr->f_lastoffset = fptr->f_length;
1833                         fptr->f_lastlength = 0;
1834                         break;
1835                         
1836                     case M_RANDOM:
1837                         fptr->f_lastoffset = fptr->f_length / 2;
1838                         fptr->f_lastlength = 0;
1839                         break;
1840                     }
1841                     
1842                     Nfiles++;
1843                 }
1844             }
1845
1846             optind++;
1847         }
1848
1849         if (Nfiles == 0) {
1850             fprintf(stderr, "iogen%s:  Could not create, or gather info for any test files\n", TagName);
1851             exit(2);
1852         }
1853     }
1854
1855     return 0;
1856 }
1857
1858 int
1859 help(stream)
1860 FILE    *stream;
1861 {
1862     usage(stream);
1863     fprintf(stream, "\n");
1864 #ifndef linux
1865     fprintf(stream, "\t-a aio_type,...  Async io completion types to choose.  Supported types\n");
1866 #ifdef CRAY
1867 #if _UMK || RELEASE_LEVEL >= 8000
1868     fprintf(stream, "\t                 are:  poll, signal, recall, recalla, and recalls.\n");
1869 #else
1870     fprintf(stream, "\t                 are:  poll, signal, recalla, and recalls.\n");
1871 #endif
1872 #else
1873     fprintf(stream, "\t                 are:  poll, signal, suspend, and callback.\n");
1874 #endif
1875     fprintf(stream, "\t                 Default is all of the above.\n");
1876 #else /* !linux */
1877     fprintf(stream, "\t-a               (Not used on Linux).\n");
1878 #endif /* !linux */
1879     fprintf(stream, "\t-f flag,...      Flags to use for file IO.  Supported flags are\n");
1880 #ifdef CRAY
1881     fprintf(stream, "\t                 raw, ssd, buffered, ldraw, sync,\n");
1882     fprintf(stream, "\t                 raw+wf, raw+wf+ldraw, raw+wf+ldraw+sync,\n");
1883     fprintf(stream, "\t                 and parallel (unicos/mk on MPP only).\n");
1884     fprintf(stream, "\t                 Default is 'raw,ldraw,sync,buffered'.\n");
1885 #else
1886 #ifndef NO_XFS
1887     fprintf(stream, "\t                 buffered, direct, sync.\n");
1888     fprintf(stream, "\t                 Default is 'buffered,sync'.\n");
1889 #else
1890     fprintf(stream, "\t                 buffered, sync.\n");
1891     fprintf(stream, "\t                 Default is 'buffered,sync'.\n");
1892 #endif /* sgi */
1893 #endif /* CRAY */
1894     fprintf(stream, "\t-h               This help.\n");
1895     fprintf(stream, "\t-i iterations[s] # of requests to generate.  0 means causes iogen\n");
1896     fprintf(stream, "\t                 to run until it's killed.  If iterations is suffixed\n");
1897     fprintf(stream, "\t                 with 's', then iterations is the number of seconds\n");
1898     fprintf(stream, "\t                 that iogen should run for.  Default is '0'.\n");
1899 #ifndef CRAY
1900     fprintf(stream, "\t-L min:max       listio nstrides / nrequests range\n");
1901 #else
1902     fprintf(stream, "\t-L               (Not used on this platform).\n");
1903 #endif /* !CRAY */
1904     fprintf(stream, "\t-m offset-mode   The mode by which iogen chooses the offset for\n");
1905     fprintf(stream, "\t                 consectutive transfers within a given file.\n");
1906     fprintf(stream, "\t                 Allowed values are 'random', 'sequential',\n");
1907     fprintf(stream, "\t                 and 'reverse'.\n");
1908     fprintf(stream, "\t                 sequential is the default.\n");
1909     fprintf(stream, "\t-N tagname       Tag name, for Monster.\n");
1910     fprintf(stream, "\t-o               Form overlapping consecutive requests.\n");
1911     fprintf(stream, "\t-O               Open flags for creating files\n");
1912 #ifdef CRAY
1913     fprintf(stream, "\t                 {O_PLACE,O_BIG,etc}[:CBITS[:CBLKS]]\n");
1914 #endif
1915 #ifndef NO_XFS
1916     fprintf(stream, "\t                 realtime:extsize - put file on real-time volume\n");
1917     fprintf(stream, "\t                 allocate - allocate space with F_ALLOCSP\n");
1918     fprintf(stream, "\t                 reserve - reserve space with F_RESVSP (default)\n");
1919     fprintf(stream, "\t                 noreserve - do not reserve with F_RESVSP\n");
1920     fprintf(stream, "\t                 direct - use O_DIRECT I/O to write to the file\n");
1921 #else
1922     fprintf(stream, "\t                 {O_SYNC,etc}\n");
1923 #endif
1924     fprintf(stream, "\t-p               Output pipe.  Default is stdout.\n");
1925     fprintf(stream, "\t-q               Quiet mode.  Normally iogen spits out info\n");
1926     fprintf(stream, "\t                 about test files, options, etc. before starting.\n");
1927     fprintf(stream, "\t-s syscall,...   Syscalls to do.  Supported syscalls are\n");
1928 #ifdef sgi
1929     fprintf(stream, "\t                 read, write, pread, pwrite, readv, writev\n");
1930     fprintf(stream, "\t                 aread, awrite, resvsp, unresvsp, ffsync,\n");
1931     fprintf(stream, "\t                 mmread, mmwrite, fsync2, fdatasync,\n");
1932     fprintf(stream, "\t                 Default is 'read,write,pread,pwrite,readv,writev,mmread,mmwrite'.\n");
1933 #endif
1934 #ifdef CRAY
1935     fprintf(stream, "\t                 read, write, reada, writea, listio,\n");
1936     fprintf(stream, "\t                 ssread (PVP only), and sswrite (PVP only).\n");
1937     fprintf(stream, "\t                 Default is 'read,write,reada,writea,listio'.\n");
1938 #endif
1939 #ifdef linux
1940     fprintf(stream, "\t                 read, write, pread, pwrite, readv, writev,\n");
1941     fprintf(stream, "\t                 mmread, mmwrite, fsync2, fdatasync,\n");
1942     fprintf(stream, "\t                 Default is 'read,write,readv,writev,mmread,mmwrite'.\n");
1943 #endif
1944     fprintf(stream, "\t-t mintrans      Min transfer length\n");
1945     fprintf(stream, "\t-T maxtrans      Max transfer length\n");
1946     fprintf(stream, "\n");
1947     fprintf(stream, "\t[len:]file,...   Test files to do IO against (note ssread/sswrite\n");
1948     fprintf(stream, "\t                 don't need a test file).  The len: syntax\n");
1949     fprintf(stream, "\t                 informs iogen to first create/expand/truncate the\n");
1950     fprintf(stream, "\t                 to the desired length.\n");
1951     fprintf(stream, "\n");
1952     fprintf(stream, "\tNote:  The ssd flag causes sds transfers to also be done.\n");
1953     fprintf(stream, "\t       To totally eliminate sds transfers, you must eleminate sds\n");
1954     fprintf(stream, "\t       from the flags (-f) and ssread,ssrite from the syscalls (-s)\n");
1955     fprintf(stream, "\tThe mintrans, maxtrans, and len: parameters are numbers of the\n");
1956     fprintf(stream, "\tform [0-9]+[bkm].  The optional trailing b, k, or m multiplies\n");
1957     fprintf(stream, "\tthe number by blocks, kilobytes, or megabytes.  If no trailing\n");
1958     fprintf(stream, "\tmultiplier is present, the number is interpreted as bytes\n");
1959
1960     return 0;
1961 }
1962
1963 /*
1964  * Obvious - usage clause
1965  */
1966
1967 int
1968 usage(stream)
1969 FILE    *stream;
1970 {
1971     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);
1972     return 0;
1973 }