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