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