2 * Copyright (c) 2000 Silicon Graphics, Inc.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * doio - a general purpose io initiator with system call and
20 * write logging. See doio.h for the structure which defines
21 * what doio requests should look like.
26 * messages should generally be printed using doio_fprintf().
33 #include <aio.h> /* for aio_read,write */
34 #include <inttypes.h> /* for uint64_t type */
35 #include <siginfo.h> /* signal handlers & SA_SIGINFO */
38 #include <sys/uio.h> /* for struct iovec (readv)*/
39 #include <sys/mman.h> /* for mmap(2) */
40 #include <sys/ipc.h> /* for i/o buffer in shared memory */
41 #include <sys/shm.h> /* for i/o buffer in shared memory */
43 #include <sys/time.h> /* for delays */
48 int do_xfsctl(struct io_req *);
53 #include "write_log.h"
54 #include "random_range.h"
55 #include "string_to_tokens.h"
58 #define O_SSD 0 /* so code compiles on a CRAY2 */
61 #define UINT64_T unsigned long long
64 #define O_PARALLEL 0 /* so O_PARALLEL may be used in expressions */
67 #define PPID_CHECK_INTERVAL 5 /* check ppid every <-- iterations */
68 #define MAX_AIO 256 /* maximum number of async I/O ops */
70 #define MPP_BUMP 16 /* page un-alignment for MPP */
76 #define SYSERR strerror(errno)
79 * getopt() string of supported cmdline arguments.
82 #define OPTS "aC:d:ehm:n:kr:w:vU:V:M:N:"
84 #define DEF_RELEASE_INTERVAL 0
87 * Flags set in parse_cmdline() to indicate which options were selected
91 int a_opt = 0; /* abort on data compare errors */
92 int e_opt = 0; /* exec() after fork()'ing */
93 int C_opt = 0; /* Data Check Type */
94 int d_opt = 0; /* delay between operations */
95 int k_opt = 0; /* lock file regions during writes */
96 int m_opt = 0; /* generate periodic messages */
97 int n_opt = 0; /* nprocs */
98 int r_opt = 0; /* resource release interval */
99 int w_opt = 0; /* file write log file */
100 int v_opt = 0; /* verify writes if set */
101 int U_opt = 0; /* upanic() on varios conditions */
102 int V_opt = 0; /* over-ride default validation fd type */
103 int M_opt = 0; /* data buffer allocation types */
104 char TagName[40]; /* name of this doio (see Monster) */
108 * Misc globals initialized in parse_cmdline()
111 char *Prog = NULL; /* set up in parse_cmdline() */
112 int Upanic_Conditions; /* set by args to -U */
113 int Release_Interval; /* arg to -r */
114 int Nprocs; /* arg to -n */
115 char *Write_Log; /* arg to -w */
116 char *Infile; /* input file (defaults to stdin) */
117 int *Children; /* pids of child procs */
119 int Nsiblings = 0; /* tfork'ed siblings */
121 int Message_Interval = 0;
122 int Npes = 0; /* non-zero if built as an mpp multi-pe app */
123 int Vpe = -1; /* Virtual pe number if Npes >= 0 */
124 int Reqno = 1; /* request # - used in some error messages */
125 int Reqskipcnt = 0; /* count of I/O requests that are skipped */
126 int Validation_Flags;
127 char *(*Data_Check)(); /* function to call for data checking */
128 int (*Data_Fill)(); /* function to call for data filling */
129 int Nmemalloc = 0; /* number of memory allocation strategies */
130 int delayop = 0; /* delay between operations - type of delay */
131 int delaytime = 0; /* delay between operations - how long */
133 struct wlog_file Wlog;
135 int active_mmap_rw = 0; /* Indicates that mmapped I/O is occurring. */
136 /* Used by sigbus_action() in the child doio. */
139 #define SKIP_REQ -2 /* skip I/O request */
142 #define MEM_DATA 1 /* data space */
143 #define MEM_SHMEM 2 /* System V shared memory */
144 #define MEM_T3ESHMEM 3 /* T3E Shared Memory */
145 #define MEM_MMAP 4 /* mmap(2) */
147 #define MEMF_PRIVATE 0001
148 #define MEMF_AUTORESRV 0002
149 #define MEMF_LOCAL 0004
150 #define MEMF_SHARED 0010
152 #define MEMF_FIXADDR 0100
153 #define MEMF_ADDR 0200
154 #define MEMF_AUTOGROW 0400
155 #define MEMF_FILE 01000 /* regular file -- unlink on close */
156 #define MEMF_MPIN 010000 /* use mpin(2) to lock pages in memory */
163 void *space; /* memory address of allocated space */
164 int fd; /* FD open for mmaping */
166 } Memalloc[NMEMALLOC];
169 * Global file descriptors
172 int Wfd_Append; /* for appending to the write-log */
173 int Wfd_Random; /* for overlaying write-log entries */
176 * Structure for maintaining open file test descriptors. Used by
181 char c_file[MAX_FNAME_LENGTH+1];
186 int c_memalign; /* from xfsctl(XFS_IOC_DIOINFO) */
191 void *c_memaddr; /* mmapped address */
192 int c_memlen; /* length of above region */
196 #define FD_ALLOC_INCR 32 /* allocate this many fd_map structs */
200 * Globals for tracking Sds and Core usage
203 char *Memptr; /* ptr to core buffer space */
204 int Memsize; /* # bytes pointed to by Memptr */
205 /* maintained by alloc_mem() */
207 int Sdsptr; /* sds offset (always 0) */
208 int Sdssize; /* # bytes of allocated sds space */
209 /* Maintained by alloc_sds() */
215 * Signal handlers, and related globals
218 void sigint_handler(); /* Catch SIGINT in parent doio, propagate
219 * to children, does not die. */
221 void die_handler(); /* Bad sig in child doios, exit 1. */
222 void cleanup_handler(); /* Normal kill, exit 0. */
225 void sigbus_handler(); /* Handle sigbus--check active_mmap_rw to
226 decide if this should be a normal exit. */
229 void cb_handler(); /* Posix aio callback handler. */
230 void noop_handler(); /* Delayop alarm, does nothing. */
234 char *format_listio();
236 int doio_fprintf(FILE *stream, char *format, ...);
241 int alloc_fd( char *, int );
242 int alloc_mem( int );
243 int do_read( struct io_req * );
244 int do_write( struct io_req * );
245 int do_rw( struct io_req * );
246 int do_sync( struct io_req * );
248 int aio_unregister( int );
249 int parse_cmdline( int, char **, char * );
250 int lock_file_region( char *, int, int, int, int );
251 struct fd_cache *alloc_fdcache(char *, int);
252 int aio_register( int, int, int );
258 * Upanic conditions, and a map from symbolics to values
261 #define U_CORRUPTION 0001 /* upanic on data corruption */
262 #define U_IOSW 0002 /* upanic on bad iosw */
263 #define U_RVAL 0004 /* upanic on bad rval */
265 #define U_ALL (U_CORRUPTION | U_IOSW | U_RVAL)
269 * Used to map cmdline arguments to values
276 struct smap Upanic_Args[] = {
277 { "corruption", U_CORRUPTION },
295 int aio_ret; /* from aio_return */
296 int aio_errno; /* from aio_error */
300 struct sigaction osa;
303 struct aio_info Aio_Info[MAX_AIO];
305 struct aio_info *aio_slot();
306 int aio_done( struct aio_info * );
308 /* -C data-fill/check type */
310 struct smap checkmap[] = {
311 { "default", C_DEFAULT },
315 /* -d option delay types */
316 #define DELAY_SELECT 1
317 #define DELAY_SLEEP 2
318 #define DELAY_SGINAP 3
319 #define DELAY_ALARM 4
320 #define DELAY_ITIMER 5 /* POSIX timer */
322 struct smap delaymap[] = {
323 { "select", DELAY_SELECT },
324 { "sleep", DELAY_SLEEP },
326 { "sginap", DELAY_SGINAP },
328 { "alarm", DELAY_ALARM },
334 * strerror() does similar actions.
339 static char sys_errno[10];
340 sprintf(sys_errno, "%d", errno);
351 int i, pid, stat, ex_stat;
354 umask(0); /* force new file modes to known values */
356 Npes = sysconf(_SC_CRAY_NPES); /* must do this before parse_cmdline */
357 Vpe = sysconf(_SC_CRAY_VPE);
361 parse_cmdline(argc, argv, OPTS);
363 random_range_seed(getpid()); /* initialize random number generator */
366 * If this is a re-exec of doio, jump directly into the doio function.
375 * Stop on all but a few signals...
377 sigemptyset(&sa.sa_mask);
378 sa.sa_handler = sigint_handler;
379 sa.sa_flags = SA_RESETHAND; /* sigint is ignored after the */
381 for (i = 1; i <= NSIG; i++) {
402 sigaction(i, &sa, NULL);
407 * If we're logging write operations, make a dummy call to wlog_open
408 * to initialize the write history file. This call must be done in
409 * the parent, to ensure that the history file exists and/or has
410 * been truncated before any children attempt to open it, as the doio
411 * children are not allowed to truncate the file.
415 strcpy(Wlog.w_file, Write_Log);
417 if (wlog_open(&Wlog, 1, 0666) < 0) {
419 "Could not create/truncate write log %s\n",
428 * Malloc space for the children pid array. Initialize all entries
432 Children = (int *)malloc(sizeof(int) * Nprocs);
433 for (i = 0; i < Nprocs; i++) {
437 omask = sigblock(sigmask(SIGCLD));
440 * Fork Nprocs. This [parent] process is a watchdog, to notify the
441 * invoker of procs which exit abnormally, and to make sure that all
442 * child procs get cleaned up. If the -e option was used, we will also
443 * re-exec. This is mostly for unicos/mk on mpp's, to ensure that not
444 * all of the doio's don't end up in the same pe.
446 * Note - if Nprocs is 1, or this doio is a multi-pe app (Npes > 1),
447 * jump directly to doio(). multi-pe apps can't fork(), and there is
448 * no reason to fork() for 1 proc.
451 if (Nprocs == 1 || Npes > 1) {
455 for (i = 0; i < Nprocs; i++) {
456 if ((pid = fork()) == -1) {
458 "(parent) Could not fork %d children: %s (%d)\n",
463 Children[Nchildren] = pid;
471 argv[0] = (char *)malloc(strlen(exec_path + 1));
472 sprintf(argv[0], "-%s", exec_path);
474 execvp(exec_path, argv);
476 "(parent) Could not execvp %s: %s (%d)\n",
477 exec_path, SYSERR, errno);
487 * Parent spins on wait(), until all children exit.
493 if ((pid = wait(&stat)) == -1) {
498 for (i = 0; i < Nchildren; i++)
499 if (Children[i] == pid)
504 if (WIFEXITED(stat)) {
505 switch (WEXITSTATUS(stat)) {
512 "(parent) pid %d exited because of an internal error\n",
514 ex_stat |= E_INTERNAL;
519 "(parent) pid %d exited because of a setup error\n",
526 "(parent) pid %d exited because of data compare errors\n",
529 ex_stat |= E_COMPARE;
538 "(parent) pid %d exited because of a usage error\n",
546 "(parent) pid %d exited with unknown status %d\n",
547 pid, WEXITSTATUS(stat));
548 ex_stat |= E_INTERNAL;
551 } else if (WIFSIGNALED(stat) && WTERMSIG(stat) != SIGINT) {
553 "(parent) pid %d terminated by signal %d\n",
554 pid, WTERMSIG(stat));
568 * main doio function. Each doio child starts here, and never returns.
574 int rval, i, infd, nbytes;
577 struct sigaction sa, def_action, ignore_action, exit_action;
579 struct sigaction sigbus_action;
582 Memsize = Sdssize = 0;
585 * Initialize the Pattern - write-type syscalls will replace Pattern[1]
586 * with the pattern passed in the request. Make sure that
587 * strlen(Pattern) is not mod 16 so that out of order words will be
591 gethostname(Host, sizeof(Host));
592 if ((cp = strchr(Host, '.')) != NULL)
595 Pattern_Length = sprintf(Pattern, "-:%d:%s:%s*", (int)getpid(), Host, Prog);
597 if (!(Pattern_Length % 16)) {
598 Pattern_Length = sprintf(Pattern, "-:%d:%s:%s**",
599 (int)getpid(), Host, Prog);
603 * Open a couple of descriptors for the write-log file. One descriptor
604 * is for appending, one for random access. Write logging is done for
605 * file corruption detection. The program doio_check is capable of
606 * doing corruption detection based on a doio write-log.
611 strcpy(Wlog.w_file, Write_Log);
613 if (wlog_open(&Wlog, 0, 0666) == -1) {
615 "Could not open write log file (%s): wlog_open() failed\n",
622 * Open the input stream - either a file or stdin
625 if (Infile == NULL) {
628 if ((infd = open(Infile, O_RDWR)) == -1) {
630 "Could not open input file (%s): %s (%d)\n",
631 Infile, SYSERR, errno);
637 * Define a set of signals that should never be masked. Receipt of
638 * these signals generally indicates a programming error, and we want
639 * a corefile at the point of error. We put SIGQUIT in this list so
640 * that ^\ will force a user core dump.
642 * Note: the handler for these should be SIG_DFL, all of them
643 * produce a corefile as the default action.
646 ignore_action.sa_handler = SIG_IGN;
647 ignore_action.sa_flags = 0;
648 sigemptyset(&ignore_action.sa_mask);
650 def_action.sa_handler = SIG_DFL;
651 def_action.sa_flags = 0;
652 sigemptyset(&def_action.sa_mask);
655 exit_action.sa_sigaction = cleanup_handler;
656 exit_action.sa_flags = SA_SIGINFO;
657 sigemptyset(&exit_action.sa_mask);
659 sa.sa_sigaction = die_handler;
660 sa.sa_flags = SA_SIGINFO;
661 sigemptyset(&sa.sa_mask);
663 sigbus_action.sa_sigaction = sigbus_handler;
664 sigbus_action.sa_flags = SA_SIGINFO;
665 sigemptyset(&sigbus_action.sa_mask);
667 exit_action.sa_handler = cleanup_handler;
668 exit_action.sa_flags = 0;
669 sigemptyset(&exit_action.sa_mask);
671 sa.sa_handler = die_handler;
673 sigemptyset(&sa.sa_mask);
676 sigbus_action.sa_handler = sigbus_handler;
677 sigbus_action.sa_flags = 0;
678 sigemptyset(&sigbus_action.sa_mask);
682 for (i = 1; i <= NSIG; i++) {
684 /* Signals to terminate program on */
686 sigaction(i, &exit_action, NULL);
690 /* This depends on active_mmap_rw */
692 sigaction(i, &sigbus_action, NULL);
696 /* Signals to Ignore... */
702 sigaction(i, &ignore_action, NULL);
705 /* Signals to trap & report & die */
708 #ifdef SIGERR /* cray only signals */
721 sigaction(i, &sa, NULL);
725 /* Default Action for all other signals */
727 sigaction(i, &def_action, NULL);
733 * Main loop - each doio proc does this until the read returns eof (0).
734 * Call the appropriate io function based on the request type.
737 while ((nbytes = read(infd, (char *)&ioreq, sizeof(ioreq)))) {
740 * Periodically check our ppid. If it is 1, the child exits to
741 * help clean up in the case that the main doio process was
745 if (Reqno && ((Reqno % PPID_CHECK_INTERVAL) == 0)) {
746 if (getppid() == 1) {
748 "Parent doio process has exited\n");
756 "read of %d bytes from input failed: %s (%d)\n",
757 sizeof(ioreq), SYSERR, errno);
762 if (nbytes != sizeof(ioreq)) {
764 "read wrong # bytes from input stream, expected %d, got %d\n",
765 sizeof(ioreq), nbytes);
770 if (ioreq.r_magic != DOIO_MAGIC) {
772 "got a bad magic # from input stream. Expected 0%o, got 0%o\n",
773 DOIO_MAGIC, ioreq.r_magic);
779 * If we're on a Release_Interval multiple, relase all ssd and
780 * core space, and close all fd's in Fd_Map[].
783 if (Reqno && Release_Interval && ! (Reqno%Release_Interval)) {
794 ssbreak(-1 * btoc(Sdssize));
803 switch (ioreq.r_type) {
806 rval = do_read(&ioreq);
811 rval = do_write(&ioreq);
834 rval = do_rw(&ioreq);
840 rval = do_ssdio(&ioreq);
844 rval = do_listio(&ioreq);
851 rval = do_xfsctl(&ioreq);
858 rval = do_sync(&ioreq);
863 "Don't know how to handle io request type %d\n",
869 if (rval == SKIP_REQ){
872 else if (rval != 0) {
875 "doio(): operation %d returned != 0\n",
880 if (Message_Interval && Reqno % Message_Interval == 0) {
881 doio_fprintf(stderr, "Info: %d requests done (%d skipped) by this process\n", Reqno, Reqskipcnt);
891 * Child exits normally
901 struct timeval tv_delay;
902 struct sigaction sa_al, sa_old;
907 tv_delay.tv_sec = delaytime / 1000000;
908 tv_delay.tv_usec = delaytime % 1000000;
909 /*doio_fprintf(stdout, "delay_select: %d %d\n",
910 tv_delay.tv_sec, tv_delay.tv_usec);*/
911 select(0, NULL, NULL, NULL, &tv_delay);
926 sa_al.sa_handler = noop_handler;
927 sigemptyset(&sa_al.sa_mask);
928 sigaction(SIGALRM, &sa_al, &sa_old);
929 sigemptyset(&al_mask);
931 sigsuspend(&al_mask);
932 sigaction(SIGALRM, &sa_old, 0);
939 * Format IO requests, returning a pointer to the formatted text.
941 * format_strat - formats the async i/o completion strategy
942 * format_rw - formats a read[a]/write[a] request
943 * format_sds - formats a ssread/sswrite request
944 * format_listio- formats a listio request
946 * ioreq is the doio io request structure.
949 struct smap sysnames[] = {
953 { "WRITEA", WRITEA },
954 { "SSREAD", SSREAD },
955 { "SSWRITE", SSWRITE },
956 { "LISTIO", LISTIO },
958 { "LREADA", LREADA },
959 { "LWRITE", LWRITE },
960 { "LWRITEA", LWRITEA },
961 { "LSREAD", LSREAD },
962 { "LSREADA", LSREADA },
963 { "LSWRITE", LSWRITE },
964 { "LSWRITEA", LSWRITEA },
966 /* Irix System Calls */
968 { "PWRITE", PWRITE },
970 { "AWRITE", AWRITE },
971 { "LLREAD", LLREAD },
972 { "LLAREAD", LLAREAD },
973 { "LLWRITE", LLWRITE },
974 { "LLAWRITE", LLAWRITE },
975 { "RESVSP", RESVSP },
976 { "UNRESVSP", UNRESVSP },
978 /* Irix and Linux System Calls */
980 { "WRITEV", WRITEV },
983 { "FSYNC2", FSYNC2 },
984 { "FDATASYNC", FDATASYNC },
989 struct smap aionames[] = {
991 { "signal", A_SIGNAL },
992 { "recall", A_RECALL },
993 { "recalla", A_RECALLA },
994 { "recalls", A_RECALLS },
995 { "suspend", A_SUSPEND },
996 { "callback", A_CALLBACK },
1002 format_oflags(int oflags)
1008 switch(oflags & 03) {
1009 case O_RDONLY: strcat(flags,"O_RDONLY,"); break;
1010 case O_WRONLY: strcat(flags,"O_WRONLY,"); break;
1011 case O_RDWR: strcat(flags,"O_RDWR,"); break;
1012 default: strcat(flags,"O_weird"); break;
1016 strcat(flags,"O_EXCL,");
1019 strcat(flags,"O_SYNC,");
1022 strcat(flags,"O_RAW,");
1023 if(oflags & O_WELLFORMED)
1024 strcat(flags,"O_WELLFORMED,");
1027 strcat(flags,"O_SSD,");
1029 if(oflags & O_LDRAW)
1030 strcat(flags,"O_LDRAW,");
1031 if(oflags & O_PARALLEL)
1032 strcat(flags,"O_PARALLEL,");
1034 strcat(flags,"O_BIG,");
1035 if(oflags & O_PLACE)
1036 strcat(flags,"O_PLACE,");
1037 if(oflags & O_ASYNC)
1038 strcat(flags,"O_ASYNC,");
1041 if(oflags & O_DIRECT)
1042 strcat(flags,"O_DIRECT,");
1044 if(oflags & O_DSYNC)
1045 strcat(flags,"O_DSYNC,");
1046 if(oflags & O_RSYNC)
1047 strcat(flags,"O_RSYNC,");
1050 return(strdup(flags));
1054 format_strat(int strategy)
1060 case A_POLL: aio_strat = "POLL"; break;
1061 case A_SIGNAL: aio_strat = "SIGNAL"; break;
1062 case A_RECALL: aio_strat = "RECALL"; break;
1063 case A_RECALLA: aio_strat = "RECALLA"; break;
1064 case A_RECALLS: aio_strat = "RECALLS"; break;
1065 case A_SUSPEND: aio_strat = "SUSPEND"; break;
1066 case A_CALLBACK: aio_strat = "CALLBACK"; break;
1067 case 0: aio_strat = "<zero>"; break;
1069 sprintf(msg, "<error:%#o>", strategy);
1070 aio_strat = strdup(msg);
1079 struct io_req *ioreq,
1091 static char *errbuf=NULL;
1092 char *aio_strat, *cp;
1093 struct read_req *readp = &ioreq->r_data.read;
1094 struct write_req *writep = &ioreq->r_data.write;
1095 struct read_req *readap = &ioreq->r_data.read;
1096 struct write_req *writeap = &ioreq->r_data.write;
1099 errbuf = (char *)malloc(32768);
1102 cp += sprintf(cp, "Request number %d\n", Reqno);
1104 switch (ioreq->r_type) {
1106 cp += sprintf(cp, "syscall: read(%d, %#lo, %d)\n",
1107 fd, (unsigned long) buffer, readp->r_nbytes);
1108 cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n",
1109 fd, readp->r_file, readp->r_oflags);
1110 cp += sprintf(cp, " read done at file offset %d\n",
1115 cp += sprintf(cp, "syscall: write(%d, %#lo, %d)\n",
1116 fd, (unsigned long) buffer, writep->r_nbytes);
1117 cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n",
1118 fd, writep->r_file, writep->r_oflags);
1119 cp += sprintf(cp, " write done at file offset %d - pattern is %s\n",
1120 writep->r_offset, pattern);
1124 aio_strat = format_strat(readap->r_aio_strat);
1126 cp += sprintf(cp, "syscall: reada(%d, %#lo, %d, %#lo, %d)\n",
1127 fd, (unsigned long) buffer, readap->r_nbytes,
1128 (unsigned long) iosw, signo);
1129 cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n",
1130 fd, readap->r_file, readp->r_oflags);
1131 cp += sprintf(cp, " reada done at file offset %d\n",
1133 cp += sprintf(cp, " async io completion strategy is %s\n",
1138 aio_strat = format_strat(writeap->r_aio_strat);
1140 cp += sprintf(cp, "syscall: writea(%d, %#lo, %d, %#lo, %d)\n",
1141 fd, (unsigned long) buffer, writeap->r_nbytes,
1142 (unsigned long) iosw, signo);
1143 cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n",
1144 fd, writeap->r_file, writeap->r_oflags);
1145 cp += sprintf(cp, " writea done at file offset %d - pattern is %s\n",
1146 writeap->r_offset, pattern);
1147 cp += sprintf(cp, " async io completion strategy is %s\n",
1159 struct io_req *ioreq,
1166 static char *errbuf=NULL;
1169 struct ssread_req *ssreadp = &ioreq->r_data.ssread;
1170 struct sswrite_req *sswritep = &ioreq->r_data.sswrite;
1173 errbuf = (char *)malloc(32768);
1176 cp += sprintf(cp, "Request number %d\n", Reqno);
1179 switch (ioreq->r_type) {
1181 cp += sprintf(cp, "syscall: ssread(%#o, %#o, %d)\n",
1182 buffer, sds, ssreadp->r_nbytes);
1186 cp += sprintf(cp, "syscall: sswrite(%#o, %#o, %d) - pattern was %s\n",
1187 buffer, sds, sswritep->r_nbytes, pattern);
1195 * Perform the various sorts of disk reads
1202 int fd, offset, nbytes, oflags, rval;
1205 struct aio_info *aiop;
1206 int aio_id, aio_strat, signo;
1209 struct fd_cache *fdc;
1213 * Initialize common fields - assumes r_oflags, r_file, r_offset, and
1214 * r_nbytes are at the same offset in the read_req and reada_req
1218 file = req->r_data.read.r_file;
1219 oflags = req->r_data.read.r_oflags;
1220 offset = req->r_data.read.r_offset;
1221 nbytes = req->r_data.read.r_nbytes;
1223 /*printf("read: %s, %#o, %d %d\n", file, oflags, offset, nbytes);*/
1226 * Grab an open file descriptor
1227 * Note: must be done before memory allocation so that the direct i/o
1228 * information is available in mem. allocate
1231 if ((fd = alloc_fd(file, oflags)) == -1)
1235 * Allocate core or sds - based on the O_SSD flag
1239 #define wtob(x) (x * sizeof(UINT64_T))
1243 if (oflags & O_SSD) {
1244 if (alloc_sds(nbytes) == -1)
1247 addr = (char *)Sdsptr;
1249 if ((rval = alloc_mem(nbytes + wtob(1) * 2 + MPP_BUMP * sizeof(UINT64_T))) < 0) {
1256 * if io is not raw, bump the offset by a random amount
1257 * to generate non-word-aligned io.
1259 if (! (req->r_data.read.r_uflags & F_WORD_ALIGNED)) {
1260 addr += random_range(0, wtob(1) - 1, 1, NULL);
1265 /* get memory alignment for using DIRECT I/O */
1266 fdc = alloc_fdcache(file, oflags);
1268 if ((rval = alloc_mem(nbytes + wtob(1) * 2 + fdc->c_memalign)) < 0) {
1275 if( (req->r_data.read.r_uflags & F_WORD_ALIGNED) ) {
1277 * Force memory alignment for Direct I/O
1279 if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) {
1280 addr += fdc->c_memalign - ((long)addr % fdc->c_memalign);
1283 addr += random_range(0, wtob(1) - 1, 1, NULL);
1286 if ((rval = alloc_mem(nbytes + wtob(1) * 2)) < 0) {
1291 #endif /* !CRAY && sgi */
1295 switch (req->r_type) {
1297 /* move to the desired file position. */
1298 if (lseek(fd, offset, SEEK_SET) == -1) {
1299 doio_fprintf(stderr,
1300 "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n",
1301 fd, offset, SYSERR, errno);
1305 if ((rval = read(fd, addr, nbytes)) == -1) {
1306 doio_fprintf(stderr,
1307 "read() request failed: %s (%d)\n%s\n",
1309 format_rw(req, fd, addr, -1, NULL, NULL));
1310 doio_upanic(U_RVAL);
1312 } else if (rval != nbytes) {
1313 doio_fprintf(stderr,
1314 "read() request returned wrong # of bytes - expected %d, got %d\n%s\n",
1316 format_rw(req, fd, addr, -1, NULL, NULL));
1317 doio_upanic(U_RVAL);
1328 /* move to the desired file position. */
1329 if (lseek(fd, offset, SEEK_SET) == -1) {
1330 doio_fprintf(stderr,
1331 "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n",
1332 fd, offset, SYSERR, errno);
1336 aio_strat = req->r_data.read.r_aio_strat;
1337 signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
1339 aio_id = aio_register(fd, aio_strat, signo);
1340 aiop = aio_slot(aio_id);
1342 if (reada(fd, addr, nbytes, &aiop->iosw, signo) == -1) {
1343 doio_fprintf(stderr, "reada() failed: %s (%d)\n%s\n",
1345 format_rw(req, fd, addr, signo, NULL, &aiop->iosw));
1346 aio_unregister(aio_id);
1347 doio_upanic(U_RVAL);
1351 * Wait for io to complete
1357 * make sure the io completed without error
1360 if (aiop->iosw.sw_count != nbytes) {
1361 doio_fprintf(stderr,
1362 "Bad iosw from reada()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n",
1365 aiop->iosw.sw_error,
1366 aiop->iosw.sw_count,
1367 format_rw(req, fd, addr, signo, NULL, &aiop->iosw));
1368 aio_unregister(aio_id);
1369 doio_upanic(U_IOSW);
1372 aio_unregister(aio_id);
1383 return 0; /* if we get here, everything went ok */
1387 * Perform the verious types of disk writes.
1394 static int pid = -1;
1395 int fd, nbytes, oflags;
1398 int logged_write, rval, got_lock;
1399 long offset, woffset = 0;
1400 char *addr, pattern, *file, *msg;
1401 struct wlog_rec wrec;
1403 int aio_strat, aio_id;
1404 struct aio_info *aiop;
1407 struct fd_cache *fdc;
1411 * Misc variable setup
1415 nbytes = req->r_data.write.r_nbytes;
1416 offset = req->r_data.write.r_offset;
1417 pattern = req->r_data.write.r_pattern;
1418 file = req->r_data.write.r_file;
1419 oflags = req->r_data.write.r_oflags;
1421 /*printf("pwrite: %s, %#o, %d %d\n", file, oflags, offset, nbytes);*/
1424 * Allocate core memory and possibly sds space. Initialize the data
1428 Pattern[0] = pattern;
1432 * Get a descriptor to do the io on
1435 if ((fd = alloc_fd(file, oflags)) == -1)
1438 /*printf("write: %d, %s, %#o, %d %d\n",
1439 fd, file, oflags, offset, nbytes);*/
1442 * Allocate SDS space for backdoor write if desired
1446 if (oflags & O_SSD) {
1448 if ((rval = alloc_mem(nbytes + wtob(1))) < 0) {
1452 (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
1453 /*pattern_fill(Memptr, nbytes, Pattern, Pattern_Length, 0);*/
1455 if (alloc_sds(nbytes) == -1)
1458 if (sswrite((long)Memptr, Sdsptr, btoc(nbytes)) == -1) {
1459 doio_fprintf(stderr, "sswrite(%d, %d, %d) failed: %s (%d)\n",
1460 (long)Memptr, Sdsptr, btoc(nbytes),
1466 addr = (char *)Sdsptr;
1468 doio_fprintf(stderr, "Invalid O_SSD flag was generated for MPP system\n");
1471 #endif /* !CRAYMPP */
1473 if ((rval = alloc_mem(nbytes + wtob(1)) < 0)) {
1480 * if io is not raw, bump the offset by a random amount
1481 * to generate non-word-aligned io.
1484 if (! (req->r_data.write.r_uflags & F_WORD_ALIGNED)) {
1485 addr += random_range(0, wtob(1) - 1, 1, NULL);
1488 (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
1489 if( addr != Memptr )
1490 memmove( addr, Memptr, nbytes);
1494 /* get memory alignment for using DIRECT I/O */
1495 fdc = alloc_fdcache(file, oflags);
1497 if ((rval = alloc_mem(nbytes + wtob(1) * 2 + fdc->c_memalign)) < 0) {
1503 if( (req->r_data.write.r_uflags & F_WORD_ALIGNED) ) {
1505 * Force memory alignment for Direct I/O
1507 if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) {
1508 addr += fdc->c_memalign - ((long)addr % fdc->c_memalign);
1511 addr += random_range(0, wtob(1) - 1, 1, NULL);
1514 (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
1515 if( addr != Memptr )
1516 memmove( addr, Memptr, nbytes);
1519 if ((rval = alloc_mem(nbytes + wtob(1) * 2)) < 0) {
1525 (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
1526 if( addr != Memptr )
1527 memmove( addr, Memptr, nbytes);
1536 if (lock_file_region(file, fd, F_WRLCK, offset, nbytes) < 0) {
1545 * Write a preliminary write-log entry. This is done so that
1546 * doio_check can do corruption detection across an interrupt/crash.
1547 * Note that w_done is set to 0. If doio_check sees this, it
1548 * re-creates the file extents as if the write completed, but does not
1549 * do any checking - see comments in doio_check for more details.
1556 wrec.w_async = (req->r_type == WRITEA) ? 1 : 0;
1557 wrec.w_oflags = oflags;
1559 wrec.w_offset = offset;
1560 wrec.w_nbytes = nbytes;
1562 wrec.w_pathlen = strlen(file);
1563 memcpy(wrec.w_path, file, wrec.w_pathlen);
1564 wrec.w_hostlen = strlen(Host);
1565 memcpy(wrec.w_host, Host, wrec.w_hostlen);
1566 wrec.w_patternlen = Pattern_Length;
1567 memcpy(wrec.w_pattern, Pattern, wrec.w_patternlen);
1571 if ((woffset = wlog_record_write(&Wlog, &wrec, -1)) == -1) {
1572 doio_fprintf(stderr,
1573 "Could not append to write-log: %s (%d)\n",
1580 switch (req->r_type ) {
1586 if (lseek(fd, offset, SEEK_SET) == -1) {
1587 doio_fprintf(stderr,
1588 "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n",
1589 fd, offset, SYSERR, errno);
1593 rval = write(fd, addr, nbytes);
1596 doio_fprintf(stderr,
1597 "write() failed: %s (%d)\n%s\n",
1599 format_rw(req, fd, addr, -1, Pattern, NULL));
1601 doio_fprintf(stderr,
1602 "write() failed: %s\n\twrite(%d, %#o, %d)\n\toffset %d, nbytes%%miniou(%d)=%d, oflags=%#o memalign=%d, addr%%memalign=%d\n",
1606 fdc->c_miniosz, nbytes%fdc->c_miniosz,
1607 oflags, fdc->c_memalign, (long)addr%fdc->c_memalign);
1609 doio_fprintf(stderr,
1610 "write() failed: %s\n\twrite(%d, %#o, %d)\n\toffset %d, nbytes%%1B=%d, oflags=%#o\n",
1613 offset, nbytes%4096, oflags);
1615 doio_upanic(U_RVAL);
1616 } else if (rval != nbytes) {
1617 doio_fprintf(stderr,
1618 "write() returned wrong # bytes - expected %d, got %d\n%s\n",
1620 format_rw(req, fd, addr, -1, Pattern, NULL));
1621 doio_upanic(U_RVAL);
1632 if (lseek(fd, offset, SEEK_SET) == -1) {
1633 doio_fprintf(stderr,
1634 "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n",
1635 fd, offset, SYSERR, errno);
1639 aio_strat = req->r_data.write.r_aio_strat;
1640 signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
1642 aio_id = aio_register(fd, aio_strat, signo);
1643 aiop = aio_slot(aio_id);
1646 * init iosw and do the async write
1649 if (writea(fd, addr, nbytes, &aiop->iosw, signo) == -1) {
1650 doio_fprintf(stderr,
1651 "writea() failed: %s (%d)\n%s\n",
1653 format_rw(req, fd, addr, -1, Pattern, NULL));
1654 doio_upanic(U_RVAL);
1655 aio_unregister(aio_id);
1660 * Wait for io to complete
1666 * check that iosw is ok
1669 if (aiop->iosw.sw_count != nbytes) {
1670 doio_fprintf(stderr,
1671 "Bad iosw from writea()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n",
1674 aiop->iosw.sw_error,
1675 aiop->iosw.sw_count,
1676 format_rw(req, fd, addr, -1, Pattern, &aiop->iosw));
1677 aio_unregister(aio_id);
1678 doio_upanic(U_IOSW);
1681 aio_unregister(aio_id);
1691 * Verify that the data was written correctly - check_file() returns
1692 * a non-null pointer which contains an error message if there are
1697 msg = check_file(file, offset, nbytes, Pattern, Pattern_Length,
1698 0, oflags & O_PARALLEL);
1700 doio_fprintf(stderr, "%s%s\n",
1703 format_rw(req, fd, addr, -1, Pattern, &aiop->iosw)
1705 format_rw(req, fd, addr, -1, Pattern, NULL)
1708 doio_upanic(U_CORRUPTION);
1715 * General cleanup ...
1717 * Write extent information to the write-log, so that doio_check can do
1718 * corruption detection. Note that w_done is set to 1, indicating that
1719 * the write has been verified as complete. We don't need to write the
1720 * filename on the second logging.
1723 if (w_opt && logged_write) {
1725 wlog_record_write(&Wlog, &wrec, woffset);
1729 * Unlock file region if necessary
1733 if (lock_file_region(file, fd, F_UNLCK, offset, nbytes) < 0) {
1739 return( (rval == -1) ? -1 : 0);
1744 * Simple routine to lock/unlock a file using fcntl()
1748 lock_file_region(fname, fd, type, start, nbytes)
1759 flk.l_start = start;
1762 if (fcntl(fd, F_SETLKW, &flk) < 0) {
1763 doio_fprintf(stderr,
1764 "fcntl(%d, %d, %#o) failed for file %s, lock type %d, offset %d, length %d: %s (%d), open flags: %#o\n",
1765 fd, F_SETLKW, &flk, fname, type,
1766 start, nbytes, SYSERR, errno,
1767 fcntl(fd, F_GETFL, 0));
1775 * Perform a listio request.
1781 struct io_req *ioreq,
1783 struct listreq *list,
1789 static char *errbuf=NULL;
1790 struct listio_req *liop = &ioreq->r_data.listio;
1791 struct listreq *listreq;
1792 char *cp, *cmd, *opcode, *aio_strat;
1796 case LC_START: cmd = "LC_START"; break;
1797 case LC_WAIT: cmd = "LC_WAIT"; break;
1798 default: cmd = "???"; break;
1802 errbuf = (char *)malloc(32768);
1805 cp += sprintf(cp, "Request number %d\n", Reqno);
1807 cp += sprintf(cp, "syscall: listio(%s, %#o, %d)\n\n",
1810 aio_strat = format_strat(liop->r_aio_strat);
1812 for (i = 0; i < nent; i++) {
1813 cp += sprintf(cp, "struct lioreq for request element %d\n", i);
1814 cp += sprintf(cp, "----------------------------------------\n");
1818 switch (listreq->li_opcode) {
1819 case LO_READ: opcode = "LO_READ"; break;
1820 case LO_WRITE: opcode = "LO_WRITE"; break;
1821 default: opcode = "???"; break;
1824 cp += sprintf(cp, " li_opcode = %s\n", opcode);
1825 cp += sprintf(cp, " li_drvr = %#o\n", listreq->li_drvr);
1826 cp += sprintf(cp, " li_flags = %#o\n", listreq->li_flags);
1827 cp += sprintf(cp, " li_offset = %d\n", listreq->li_offset);
1828 cp += sprintf(cp, " li_fildes = %d\n", listreq->li_fildes);
1829 cp += sprintf(cp, " li_buf = %#o\n", listreq->li_buf);
1830 cp += sprintf(cp, " li_nbyte = %d\n", listreq->li_nbyte);
1831 cp += sprintf(cp, " li_status = %#o (%d, %d, %d)\n", listreq->li_status, listreq->li_status->sw_flag, listreq->li_status->sw_error, listreq->li_status->sw_count);
1832 cp += sprintf(cp, " li_signo = %d\n", listreq->li_signo);
1833 cp += sprintf(cp, " li_nstride = %d\n", listreq->li_nstride);
1834 cp += sprintf(cp, " li_filstride = %d\n", listreq->li_filstride);
1835 cp += sprintf(cp, " li_memstride = %d\n", listreq->li_memstride);
1836 cp += sprintf(cp, " io completion strategy is %s\n", aio_strat);
1847 struct listio_req *lio;
1848 int fd, oflags, signo, nb, i;
1849 int logged_write, rval, got_lock;
1850 int aio_strat, aio_id;
1851 int min_byte, max_byte;
1853 int foffset, fstride, mstride, nstrides;
1855 long offset, woffset;
1857 sigset_t block_mask, omask;
1858 struct wlog_rec wrec;
1859 struct aio_info *aiop;
1860 struct listreq lio_req;
1862 lio = &req->r_data.listio;
1865 * If bytes per stride is less than the stride size, drop the request
1866 * since it will cause overlapping strides, and we cannot predict
1867 * the order they will complete in.
1870 if (lio->r_filestride && abs(lio->r_filestride) < lio->r_nbytes) {
1871 doio_fprintf(stderr, "do_listio(): Bogus listio request - abs(filestride) [%d] < nbytes [%d]\n",
1872 abs(lio->r_filestride), lio->r_nbytes);
1877 * Allocate core memory. Initialize the data to be written. Make
1878 * sure we get enough, based on the memstride.
1882 stride_bounds(0, lio->r_memstride, lio->r_nstrides,
1883 lio->r_nbytes, NULL, NULL);
1885 if ((rval = alloc_mem(mem_needed + wtob(1))) < 0) {
1890 * Set the memory address pointer. If the io is not raw, adjust
1891 * addr by a random amount, so that non-raw io is not necessarily
1897 if (! (lio->r_uflags & F_WORD_ALIGNED)) {
1898 addr += random_range(0, wtob(1) - 1, 1, NULL);
1901 if (lio->r_opcode == LO_WRITE) {
1902 Pattern[0] = lio->r_pattern;
1903 (*Data_Fill)(Memptr, mem_needed, Pattern, Pattern_Length, 0);
1904 if( addr != Memptr )
1905 memmove( addr, Memptr, mem_needed);
1909 * Get a descriptor to do the io on. No need to do an lseek, as this
1910 * is encoded in the listio request.
1913 if ((fd = alloc_fd(lio->r_file, lio->r_oflags)) == -1) {
1922 * If the opcode is LO_WRITE, lock all regions of the file that
1923 * are touched by this listio request. Currently, we use
1924 * stride_bounds() to figure out the min and max bytes affected, and
1925 * lock the entire region, regardless of the file stride.
1928 if (lio->r_opcode == LO_WRITE && k_opt) {
1929 stride_bounds(lio->r_offset,
1930 lio->r_filestride, lio->r_nstrides,
1931 lio->r_nbytes, &min_byte, &max_byte);
1933 if (lock_file_region(lio->r_file, fd, F_WRLCK,
1934 min_byte, (max_byte-min_byte+1)) < 0) {
1935 doio_fprintf(stderr, "stride_bounds(%d, %d, %d, %d, ..., ...) set min_byte to %d, max_byte to %d\n",
1936 lio->r_offset, lio->r_filestride,
1937 lio->r_nstrides, lio->r_nbytes, min_byte,
1949 aio_strat = lio->r_aio_strat;
1950 signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
1952 aio_id = aio_register(fd, aio_strat, signo);
1953 aiop = aio_slot(aio_id);
1956 * Form the listio request, and make the call.
1959 lio_req.li_opcode = lio->r_opcode;
1960 lio_req.li_drvr = 0;
1961 lio_req.li_flags = LF_LSEEK;
1962 lio_req.li_offset = lio->r_offset;
1963 lio_req.li_fildes = fd;
1965 if (lio->r_memstride >= 0 || lio->r_nstrides <= 1) {
1966 lio_req.li_buf = addr;
1968 lio_req.li_buf = addr + mem_needed - lio->r_nbytes;
1971 lio_req.li_nbyte = lio->r_nbytes;
1972 lio_req.li_status = &aiop->iosw;
1973 lio_req.li_signo = signo;
1974 lio_req.li_nstride = lio->r_nstrides;
1975 lio_req.li_filstride = lio->r_filestride;
1976 lio_req.li_memstride = lio->r_memstride;
1979 * If signo != 0, block signo while we're in the system call, so that
1980 * we don't get interrupted syscall failures.
1984 sigemptyset(&block_mask);
1985 sigaddset(&block_mask, signo);
1986 sigprocmask(SIG_BLOCK, &block_mask, &omask);
1989 if (listio(lio->r_cmd, &lio_req, 1) < 0) {
1990 doio_fprintf(stderr,
1991 "listio() failed: %s (%d)\n%s\n",
1993 format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern));
1994 aio_unregister(aio_id);
1995 doio_upanic(U_RVAL);
2000 sigprocmask(SIG_SETMASK, &omask, NULL);
2004 * Wait for io to complete
2009 nstrides = lio->r_nstrides ? lio->r_nstrides : 1;
2010 if (aiop->iosw.sw_count != lio->r_nbytes * nstrides) {
2011 doio_fprintf(stderr,
2012 "Bad iosw from listio()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n",
2013 1, 0, lio->r_nbytes * lio->r_nstrides,
2015 aiop->iosw.sw_error, aiop->iosw.sw_count,
2016 format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern));
2017 aio_unregister(aio_id);
2018 doio_upanic(U_IOSW);
2022 aio_unregister(aio_id);
2025 * Verify that the data was written correctly - check_file() returns
2026 * a non-null pointer which contains an error message if there are
2029 * For listio, we basically have to make 1 call to check_file for each
2033 if (v_opt && lio_req.li_opcode == LO_WRITE) {
2034 fstride = lio->r_filestride ? lio->r_filestride : lio->r_nbytes;
2035 mstride = lio->r_memstride ? lio->r_memstride : lio->r_nbytes;
2036 foffset = lio->r_offset;
2038 if (mstride> 0 || lio->r_nstrides <= 1) {
2041 moffset = addr + mem_needed - lio->r_nbytes;
2044 for (i = 0; i < lio_req.li_nstride; i++) {
2045 msg = check_file(lio->r_file,
2046 foffset, lio->r_nbytes,
2047 Pattern, Pattern_Length,
2049 lio->r_oflags & O_PARALLEL);
2052 doio_fprintf(stderr, "%s\n%s\n",
2054 format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern));
2055 doio_upanic(U_CORRUPTION);
2070 * General cleanup ...
2075 * Release file locks if necessary
2079 if (lock_file_region(lio->r_file, fd, F_UNLCK,
2080 min_byte, (max_byte-min_byte+1)) < 0) {
2092 * perform ssread/sswrite operations
2104 nbytes = req->r_data.ssread.r_nbytes;
2107 * Grab core and sds space
2110 if ((nb = alloc_mem(nbytes)) < 0)
2113 if (alloc_sds(nbytes) == -1)
2116 if (req->r_type == SSWRITE) {
2119 * Init data and ship it to the ssd
2122 Pattern[0] = req->r_data.sswrite.r_pattern;
2123 /*pattern_fill(Memptr, nbytes, Pattern, Pattern_Length, 0);*/
2124 (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
2126 if (sswrite((long)Memptr, (long)Sdsptr, btoc(nbytes)) == -1) {
2127 doio_fprintf(stderr, "sswrite() failed: %s (%d)\n%s\n",
2129 format_sds(req, Memptr, Sdsptr, Pattern));
2130 doio_upanic(U_RVAL);
2138 if (ssread((long)Memptr, (long)Sdsptr, btoc(nbytes)) == -1) {
2139 doio_fprintf(stderr, "ssread() failed: %s (%d)\n%s\n",
2141 format_sds(req, Memptr, Sdsptr, Pattern));
2143 doio_upanic(U_RVAL);
2149 * Verify data if SSWRITE and v_opt
2152 if (v_opt && req->r_type == SSWRITE) {
2153 ssread((long)Memptr, (long)Sdsptr, btoc(nbytes));
2155 if (pattern_check(Memptr, nbytes, Pattern, Pattern_Length, 0) == -1) {
2156 doio_fprintf(stderr,
2157 "sds DATA COMPARE ERROR - ABORTING\n%s\n",
2158 format_sds(req, Memptr, Sdsptr, Pattern));
2160 doio_upanic(U_CORRUPTION);
2174 doio_fprintf(stderr,
2175 "Internal Error - do_ssdio() called on a non-cray1 system\n");
2185 /* ---------------------------------------------------------------------------
2187 * A new paradigm of doing the r/w system call where there is a "stub"
2188 * function that builds the info for the system call, then does the system
2189 * call; this is called by code that is common to all system calls and does
2190 * the syscall return checking, async I/O wait, iosw check, etc.
2193 * WRITE, ASYNC, SSD/SDS,
2194 * FILE_LOCK, WRITE_LOG, VERIFY_DATA,
2198 int rval; /* syscall return */
2199 int err; /* errno */
2200 int *aioid; /* list of async I/O structures */
2203 struct syscall_info {
2206 struct status *(*sy_syscall)();
2208 char *(*sy_format)();
2213 #define SY_WRITE 00001
2214 #define SY_ASYNC 00010
2215 #define SY_IOSW 00020
2216 #define SY_SDS 00100
2219 fmt_ioreq(struct io_req *ioreq, struct syscall_info *sy, int fd)
2221 static char *errbuf=NULL;
2230 errbuf = (char *)malloc(32768);
2232 io = &ioreq->r_data.io;
2235 * Look up async I/O completion strategy
2238 aname->value != -1 && aname->value != io->r_aio_strat;
2243 cp += sprintf(cp, "Request number %d\n", Reqno);
2245 cp += sprintf(cp, " fd %d is file %s - open flags are %#o %s\n",
2246 fd, io->r_file, io->r_oflags, format_oflags(io->r_oflags));
2248 if(sy->sy_flags & SY_WRITE) {
2249 cp += sprintf(cp, " write done at file offset %d - pattern is %c (%#o)\n",
2251 (io->r_pattern == '\0') ? '?' : io->r_pattern,
2254 cp += sprintf(cp, " read done at file offset %d\n",
2258 if(sy->sy_flags & SY_ASYNC) {
2259 cp += sprintf(cp, " async io completion strategy is %s\n",
2263 cp += sprintf(cp, " number of requests is %d, strides per request is %d\n",
2264 io->r_nent, io->r_nstrides);
2266 cp += sprintf(cp, " i/o byte count = %d\n",
2269 cp += sprintf(cp, " memory alignment is %s\n",
2270 (io->r_uflags & F_WORD_ALIGNED) ? "aligned" : "unaligned");
2273 if(io->r_oflags & O_RAW) {
2274 cp += sprintf(cp, " RAW I/O: offset %% 4096 = %d length %% 4096 = %d\n",
2275 io->r_offset % 4096, io->r_nbytes % 4096);
2277 cp += sprintf(cp, " optimal file xfer size: small: %d large: %d\n",
2278 sbuf.st_blksize, sbuf.st_oblksize);
2279 cp += sprintf(cp, " cblks %d cbits %#o\n",
2280 sbuf.st_cblks, sbuf.st_cbits);
2284 if(io->r_oflags & O_DIRECT) {
2285 struct dioattr finfo;
2287 if(xfsctl(io->r_file, fd, XFS_IOC_DIOINFO, &finfo) == -1) {
2288 cp += sprintf(cp, " Error %s (%d) getting direct I/O info\n",
2289 strerror(errno), errno);
2291 finfo.d_miniosz = 1;
2292 finfo.d_maxiosz = 1;
2295 cp += sprintf(cp, " DIRECT I/O: offset %% %d = %d length %% %d = %d\n",
2297 io->r_offset % finfo.d_miniosz,
2299 io->r_nbytes % finfo.d_miniosz);
2300 cp += sprintf(cp, " mem alignment 0x%x xfer size: small: %d large: %d\n",
2301 finfo.d_mem, finfo.d_miniosz, finfo.d_maxiosz);
2309 * Issue listio requests
2313 sy_listio(req, sysc, fd, addr)
2315 struct syscall_info *sysc;
2319 int offset, nbytes, nstrides, nents, aio_strat;
2320 int aio_id, signo, o, i, lc;
2322 struct listreq *lio_req, *l;
2323 struct aio_info *aiop;
2324 struct status *status;
2327 * Initialize common fields - assumes r_oflags, r_file, r_offset, and
2328 * r_nbytes are at the same offset in the read_req and reada_req
2331 offset = req->r_data.io.r_offset;
2332 nbytes = req->r_data.io.r_nbytes;
2333 nstrides = req->r_data.io.r_nstrides;
2334 nents = req->r_data.io.r_nent;
2335 aio_strat = req->r_data.io.r_aio_strat;
2337 lc = (sysc->sy_flags & SY_ASYNC) ? LC_START : LC_WAIT;
2339 status = (struct status *)malloc(sizeof(struct status));
2340 if( status == NULL ){
2341 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2342 __FILE__, __LINE__);
2345 status->aioid = (int *)malloc( (nents+1) * sizeof(int) );
2346 if( status->aioid == NULL ){
2347 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2348 __FILE__, __LINE__);
2352 signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
2354 lio_req = (struct listreq *)malloc(nents * sizeof(struct listreq));
2355 if( lio_req == NULL ){
2356 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2357 __FILE__, __LINE__);
2360 for(l=lio_req,a=addr,o=offset,i=0;
2362 l++, a+=nbytes, o+=nbytes, i++) {
2364 aio_id = aio_register(fd, aio_strat, signo);
2365 aiop = aio_slot(aio_id);
2366 status->aioid[i] = aio_id;
2368 l->li_opcode = (sysc->sy_flags & SY_WRITE) ? LO_WRITE : LO_READ;
2372 l->li_nbyte = nbytes;
2373 l->li_status = &aiop->iosw;
2374 l->li_signo = signo;
2375 l->li_nstride = nstrides;
2376 l->li_filstride = 0;
2377 l->li_memstride = 0;
2379 l->li_flags = LF_LSEEK;
2382 status->aioid[nents] = -1; /* end sentinel */
2384 if( (status->rval = listio(lc, lio_req, nents)) == -1) {
2385 status->err = errno;
2393 * Calculate the size of a request in bytes and min/max boundaries
2395 * This assumes filestride & memstride = 0.
2398 listio_mem(struct io_req *req, int offset, int fmstride,
2403 size = stride_bounds(offset, fmstride,
2404 req->r_data.io.r_nstrides*req->r_data.io.r_nent,
2405 req->r_data.io.r_nbytes, min, max);
2410 fmt_listio(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
2412 static char *errbuf = NULL;
2418 errbuf = (char *)malloc(32768);
2419 if( errbuf == NULL ){
2420 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2421 __FILE__, __LINE__);
2426 c = (sy->sy_flags & SY_ASYNC) ? "lc_wait" : "lc_start";
2429 cp += sprintf(cp, "syscall: listio(%s, (?), %d)\n",
2430 c, req->r_data.io.r_nent);
2432 cp += sprintf(cp, " data buffer at %#o\n", addr);
2439 sy_pread(req, sysc, fd, addr)
2441 struct syscall_info *sysc;
2446 struct status *status;
2448 rc = pread(fd, addr, req->r_data.io.r_nbytes,
2449 req->r_data.io.r_offset);
2451 status = (struct status *)malloc(sizeof(struct status));
2452 if( status == NULL ){
2453 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2454 __FILE__, __LINE__);
2457 status->aioid = NULL;
2459 status->err = errno;
2465 sy_pwrite(req, sysc, fd, addr)
2467 struct syscall_info *sysc;
2472 struct status *status;
2474 rc = pwrite(fd, addr, req->r_data.io.r_nbytes,
2475 req->r_data.io.r_offset);
2477 status = (struct status *)malloc(sizeof(struct status));
2478 if( status == NULL ){
2479 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2480 __FILE__, __LINE__);
2483 status->aioid = NULL;
2485 status->err = errno;
2491 fmt_pread(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
2493 static char *errbuf = NULL;
2497 errbuf = (char *)malloc(32768);
2498 if( errbuf == NULL ){
2499 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2500 __FILE__, __LINE__);
2506 cp += sprintf(cp, "syscall: %s(%d, 0x%p, %d)\n",
2507 sy->sy_name, fd, addr, req->r_data.io.r_nbytes);
2513 sy_readv(req, sysc, fd, addr)
2515 struct syscall_info *sysc;
2519 struct status *sy_rwv();
2520 return sy_rwv(req, sysc, fd, addr, 0);
2524 sy_writev(req, sysc, fd, addr)
2526 struct syscall_info *sysc;
2530 struct status *sy_rwv();
2531 return sy_rwv(req, sysc, fd, addr, 1);
2535 sy_rwv(req, sysc, fd, addr, rw)
2537 struct syscall_info *sysc;
2543 struct status *status;
2544 struct iovec iov[2];
2546 status = (struct status *)malloc(sizeof(struct status));
2547 if( status == NULL ){
2548 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2549 __FILE__, __LINE__);
2552 status->aioid = NULL;
2554 /* move to the desired file position. */
2555 if ((rc=lseek(fd, req->r_data.io.r_offset, SEEK_SET)) == -1) {
2557 status->err = errno;
2561 iov[0].iov_base = addr;
2562 iov[0].iov_len = req->r_data.io.r_nbytes;
2565 rc = writev(fd, iov, 1);
2567 rc = readv(fd, iov, 1);
2568 status->aioid = NULL;
2570 status->err = errno;
2575 fmt_readv(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
2577 static char errbuf[32768];
2581 cp += sprintf(cp, "syscall: %s(%d, (iov on stack), 1)\n",
2589 sy_aread(req, sysc, fd, addr)
2591 struct syscall_info *sysc;
2595 struct status *sy_arw();
2596 return sy_arw(req, sysc, fd, addr, 0);
2600 sy_awrite(req, sysc, fd, addr)
2602 struct syscall_info *sysc;
2606 struct status *sy_arw();
2607 return sy_arw(req, sysc, fd, addr, 1);
2611 #define sy_aread(A, B, C, D) sy_arw(A, B, C, D, 0)
2612 #define sy_awrite(A, B, C, D) sy_arw(A, B, C, D, 1)
2616 sy_arw(req, sysc, fd, addr, rw)
2618 struct syscall_info *sysc;
2623 /* POSIX 1003.1b-1993 Async read */
2624 struct status *status;
2626 int aio_id, aio_strat, signo;
2627 struct aio_info *aiop;
2629 status = (struct status *)malloc(sizeof(struct status));
2630 if( status == NULL ){
2631 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2632 __FILE__, __LINE__);
2635 aio_strat = req->r_data.io.r_aio_strat;
2636 signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
2638 aio_id = aio_register(fd, aio_strat, signo);
2639 aiop = aio_slot(aio_id);
2641 memset( (void *)&aiop->aiocb, 0, sizeof(aiocb_t));
2643 aiop->aiocb.aio_fildes = fd;
2644 aiop->aiocb.aio_nbytes = req->r_data.io.r_nbytes;
2645 aiop->aiocb.aio_offset = req->r_data.io.r_offset;
2646 aiop->aiocb.aio_buf = addr;
2647 aiop->aiocb.aio_reqprio = 0; /* must be 0 */
2648 aiop->aiocb.aio_lio_opcode = 0;
2650 if(aio_strat == A_SIGNAL) { /* siginfo(2) stuff */
2651 aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
2652 aiop->aiocb.aio_sigevent.sigev_signo = signo;
2653 } else if(aio_strat == A_CALLBACK) {
2654 aiop->aiocb.aio_sigevent.sigev_signo = 0;
2655 aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_CALLBACK;
2656 aiop->aiocb.aio_sigevent.sigev_func = cb_handler;
2657 aiop->aiocb.aio_sigevent.sigev_value.sival_int = aio_id;
2659 aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_NONE;
2660 aiop->aiocb.aio_sigevent.sigev_signo = 0;
2664 rc = aio_write(&aiop->aiocb);
2666 rc = aio_read(&aiop->aiocb);
2668 status->aioid = (int *)malloc( 2 * sizeof(int) );
2669 if( status->aioid == NULL ){
2670 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2671 __FILE__, __LINE__);
2674 status->aioid[0] = aio_id;
2675 status->aioid[1] = -1;
2677 status->err = errno;
2682 fmt_aread(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
2684 static char errbuf[32768];
2688 cp += sprintf(cp, "syscall: %s(&aiop->aiocb)\n",
2697 sy_mmread(req, sysc, fd, addr)
2699 struct syscall_info *sysc;
2703 struct status *sy_mmrw();
2704 return sy_mmrw(req, sysc, fd, addr, 0);
2708 sy_mmwrite(req, sysc, fd, addr)
2710 struct syscall_info *sysc;
2714 struct status *sy_mmrw();
2715 return sy_mmrw(req, sysc, fd, addr, 1);
2719 sy_mmrw(req, sysc, fd, addr, rw)
2721 struct syscall_info *sysc;
2728 * This version is oriented towards mmaping the file to memory
2729 * ONCE and keeping it mapped.
2731 struct status *status;
2732 void *mrc, *memaddr;
2733 struct fd_cache *fdc;
2736 status = (struct status *)malloc(sizeof(struct status));
2737 if( status == NULL ){
2738 doio_fprintf(stderr, "malloc failed, %s/%d\n",
2739 __FILE__, __LINE__);
2742 status->aioid = NULL;
2745 fdc = alloc_fdcache(req->r_data.io.r_file, req->r_data.io.r_oflags);
2747 if( fdc->c_memaddr == NULL ) {
2748 if( fstat(fd, &sbuf) < 0 ){
2749 doio_fprintf(stderr, "fstat failed, errno=%d\n",
2751 status->err = errno;
2755 fdc->c_memlen = (int)sbuf.st_size;
2756 mrc = mmap(NULL, (int)sbuf.st_size,
2757 rw ? PROT_WRITE|PROT_READ : PROT_READ,
2760 if( mrc == MAP_FAILED ) {
2761 doio_fprintf(stderr, "mmap() failed - 0x%lx %d\n",
2763 status->err = errno;
2767 fdc->c_memaddr = mrc;
2770 memaddr = (void *)((char *)fdc->c_memaddr + req->r_data.io.r_offset);
2774 memcpy(memaddr, addr, req->r_data.io.r_nbytes);
2776 memcpy(addr, memaddr, req->r_data.io.r_nbytes);
2779 status->rval = req->r_data.io.r_nbytes;
2785 fmt_mmrw(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
2787 static char errbuf[32768];
2789 struct fd_cache *fdc;
2792 fdc = alloc_fdcache(req->r_data.io.r_file, req->r_data.io.r_oflags);
2795 cp += sprintf(cp, "syscall: %s(NULL, %d, %s, MAP_SHARED, %d, 0)\n",
2798 (sy->sy_flags & SY_WRITE) ? "PROT_WRITE" : "PROT_READ",
2801 cp += sprintf(cp, "\tfile is mmaped to: 0x%lx\n",
2802 (unsigned long) fdc->c_memaddr);
2804 memaddr = (void *)((char *)fdc->c_memaddr + req->r_data.io.r_offset);
2806 cp += sprintf(cp, "\tfile-mem=0x%lx, length=%d, buffer=0x%lx\n",
2807 (unsigned long) memaddr, req->r_data.io.r_nbytes,
2808 (unsigned long) addr);
2814 struct syscall_info syscalls[] = {
2816 { "listio-read-sync", LREAD,
2817 sy_listio, NULL, fmt_listio,
2820 { "listio-read-strides-sync", LSREAD,
2821 sy_listio, listio_mem, fmt_listio,
2824 { "listio-read-reqs-sync", LEREAD,
2825 sy_listio, listio_mem, fmt_listio,
2828 { "listio-read-async", LREADA,
2829 sy_listio, NULL, fmt_listio,
2832 { "listio-read-strides-async", LSREADA,
2833 sy_listio, listio_mem, fmt_listio,
2836 { "listio-read-reqs-async", LEREADA,
2837 sy_listio, listio_mem, fmt_listio,
2840 { "listio-write-sync", LWRITE,
2841 sy_listio, listio_mem, fmt_listio,
2844 { "listio-write-strides-sync", LSWRITE,
2845 sy_listio, listio_mem, fmt_listio,
2848 { "listio-write-reqs-sync", LEWRITE,
2849 sy_listio, listio_mem, fmt_listio,
2852 { "listio-write-async", LWRITEA,
2853 sy_listio, listio_mem, fmt_listio,
2854 SY_IOSW | SY_WRITE | SY_ASYNC
2856 { "listio-write-strides-async", LSWRITEA,
2857 sy_listio, listio_mem, fmt_listio,
2858 SY_IOSW | SY_WRITE | SY_ASYNC
2860 { "listio-write-reqs-async", LEWRITEA,
2861 sy_listio, listio_mem, fmt_listio,
2862 SY_IOSW | SY_WRITE | SY_ASYNC
2868 sy_aread, NULL, fmt_aread,
2872 sy_awrite, NULL, fmt_aread,
2873 SY_IOSW | SY_WRITE | SY_ASYNC
2877 sy_pread, NULL, fmt_pread,
2881 sy_pwrite, NULL, fmt_pread,
2887 sy_readv, NULL, fmt_readv,
2891 sy_writev, NULL, fmt_readv,
2894 { "mmap-read", MMAPR,
2895 sy_mmread, NULL, fmt_mmrw,
2898 { "mmap-write", MMAPW,
2899 sy_mmwrite, NULL, fmt_mmrw,
2914 static int pid = -1;
2915 int fd, offset, nbytes, nstrides, nents, oflags;
2916 int rval, mem_needed, i;
2917 int logged_write, got_lock, woffset = 0, pattern;
2918 int min_byte, max_byte;
2919 char *addr, *file, *msg;
2921 struct wlog_rec wrec;
2922 struct syscall_info *sy;
2924 struct aio_info *aiop;
2931 struct fd_cache *fdc;
2935 * Initialize common fields - assumes r_oflags, r_file, r_offset, and
2936 * r_nbytes are at the same offset in the read_req and reada_req
2939 file = req->r_data.io.r_file;
2940 oflags = req->r_data.io.r_oflags;
2941 offset = req->r_data.io.r_offset;
2942 nbytes = req->r_data.io.r_nbytes;
2943 nstrides= req->r_data.io.r_nstrides;
2944 nents = req->r_data.io.r_nent;
2945 pattern = req->r_data.io.r_pattern;
2947 if( nents >= MAX_AIO ) {
2948 doio_fprintf(stderr, "do_rw: too many list requests, %d. Maximum is %d\n",
2954 * look up system call info
2956 for(sy=syscalls; sy->sy_name != NULL && sy->sy_type != req->r_type; sy++)
2959 if(sy->sy_name == NULL) {
2960 doio_fprintf(stderr, "do_rw: unknown r_type %d.\n",
2966 * Get an open file descriptor
2967 * Note: must be done before memory allocation so that the direct i/o
2968 * information is available in mem. allocate
2971 if ((fd = alloc_fd(file, oflags)) == -1)
2975 * Allocate core memory and possibly sds space. Initialize the
2976 * data to be written. Make sure we get enough, based on the
2980 * 1 extra word for possible partial-word address "bump"
2981 * 1 extra word for dynamic pattern overrun
2982 * MPP_BUMP extra words for T3E non-hw-aligned memory address.
2985 if( sy->sy_buffer != NULL ) {
2986 mem_needed = (*sy->sy_buffer)(req, 0, 0, NULL, NULL);
2988 mem_needed = nbytes;
2992 if ((rval = alloc_mem(mem_needed + wtob(1) * 2 + MPP_BUMP * sizeof(UINT64_T))) < 0) {
2997 /* get memory alignment for using DIRECT I/O */
2998 fdc = alloc_fdcache(file, oflags);
3000 if ((rval = alloc_mem(mem_needed + wtob(1) * 2 + fdc->c_memalign)) < 0) {
3004 if ((rval = alloc_mem(mem_needed + wtob(1) * 2)) < 0) {
3010 Pattern[0] = pattern;
3013 * Allocate SDS space for backdoor write if desired
3016 if (oflags & O_SSD) {
3019 if (alloc_sds(nbytes) == -1)
3022 if( sy->sy_flags & SY_WRITE ) {
3023 /*pattern_fill(Memptr, mem_needed, Pattern, Pattern_Length, 0);*/
3024 (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
3026 if (sswrite((long)Memptr, Sdsptr, btoc(mem_needed)) == -1) {
3027 doio_fprintf(stderr, "sswrite(%d, %d, %d) failed: %s (%d)\n",
3028 (long)Memptr, Sdsptr,
3029 btoc(mem_needed), SYSERR, errno);
3035 addr = (char *)Sdsptr;
3037 doio_fprintf(stderr, "Invalid O_SSD flag was generated for MPP system\n");
3040 #endif /* _CRAYMPP */
3042 doio_fprintf(stderr, "Invalid O_SSD flag was generated for non-Cray system\n");
3050 * if io is not raw, bump the offset by a random amount
3051 * to generate non-word-aligned io.
3053 * On MPP systems, raw I/O must start on an 0x80 byte boundary.
3054 * For non-aligned I/O, bump the address from 1 to 8 words.
3057 if (! (req->r_data.io.r_uflags & F_WORD_ALIGNED)) {
3059 addr += random_range(0, MPP_BUMP, 1, NULL) * sizeof(int);
3061 addr += random_range(0, wtob(1) - 1, 1, NULL);
3066 * Force memory alignment for Direct I/O
3068 if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) {
3069 addr += fdc->c_memalign - ((long)addr % fdc->c_memalign);
3074 * FILL must be done on a word-aligned buffer.
3075 * Call the fill function with Memptr which is aligned,
3076 * then memmove it to the right place.
3078 if (sy->sy_flags & SY_WRITE) {
3079 (*Data_Fill)(Memptr, mem_needed, Pattern, Pattern_Length, 0);
3080 if( addr != Memptr )
3081 memmove( addr, Memptr, mem_needed);
3090 * Lock data if this is a write and locking option is set
3092 if (sy->sy_flags & SY_WRITE && k_opt) {
3093 if( sy->sy_buffer != NULL ) {
3094 (*sy->sy_buffer)(req, offset, 0, &min_byte, &max_byte);
3097 max_byte = offset + (nbytes * nstrides * nents);
3100 if (lock_file_region(file, fd, F_WRLCK,
3101 min_byte, (max_byte-min_byte+1)) < 0) {
3102 doio_fprintf(stderr,
3103 "file lock failed:\n%s\n",
3104 fmt_ioreq(req, sy, fd));
3105 doio_fprintf(stderr,
3106 " buffer(req, %d, 0, 0x%x, 0x%x)\n",
3107 offset, min_byte, max_byte);
3116 * Write a preliminary write-log entry. This is done so that
3117 * doio_check can do corruption detection across an interrupt/crash.
3118 * Note that w_done is set to 0. If doio_check sees this, it
3119 * re-creates the file extents as if the write completed, but does not
3120 * do any checking - see comments in doio_check for more details.
3123 if (sy->sy_flags & SY_WRITE && w_opt) {
3128 wrec.w_async = (sy->sy_flags & SY_ASYNC) ? 1 : 0;
3129 wrec.w_oflags = oflags;
3131 wrec.w_offset = offset;
3132 wrec.w_nbytes = nbytes; /* mem_needed -- total length */
3134 wrec.w_pathlen = strlen(file);
3135 memcpy(wrec.w_path, file, wrec.w_pathlen);
3136 wrec.w_hostlen = strlen(Host);
3137 memcpy(wrec.w_host, Host, wrec.w_hostlen);
3138 wrec.w_patternlen = Pattern_Length;
3139 memcpy(wrec.w_pattern, Pattern, wrec.w_patternlen);
3143 if ((woffset = wlog_record_write(&Wlog, &wrec, -1)) == -1) {
3144 doio_fprintf(stderr,
3145 "Could not append to write-log: %s (%d)\n",
3152 s = (*sy->sy_syscall)(req, sy, fd, addr);
3154 if( s->rval == -1 ) {
3155 doio_fprintf(stderr,
3156 "%s() request failed: %s (%d)\n%s\n%s\n",
3157 sy->sy_name, SYSERR, errno,
3158 fmt_ioreq(req, sy, fd),
3159 (*sy->sy_format)(req, sy, fd, addr));
3161 doio_upanic(U_RVAL);
3163 for(i=0; i < nents; i++) {
3164 if(s->aioid == NULL)
3166 aio_unregister(s->aioid[i]);
3171 * If the syscall was async, wait for I/O to complete
3174 if(sy->sy_flags & SY_ASYNC) {
3175 for(i=0; i < nents; i++) {
3176 aio_wait(s->aioid[i]);
3182 * Check the syscall how-much-data-written return. Look
3183 * for this in either the return value or the 'iosw'
3187 if( sy->sy_flags & SY_IOSW ) {
3189 for( i=0; i < nents; i++ ) {
3190 if(s->aioid == NULL)
3191 break; /* >>> error condition? */
3192 aiop = aio_slot(s->aioid[i]);
3194 if(iosw->sw_error != 0) {
3195 doio_fprintf(stderr,
3196 "%s() iosw error set: %s\n%s\n%s\n",
3198 strerror(iosw->sw_error),
3199 fmt_ioreq(req, sy, fd),
3200 (*sy->sy_format)(req, sy, fd, addr));
3201 doio_upanic(U_IOSW);
3203 } else if(iosw->sw_count != nbytes*nstrides) {
3204 doio_fprintf(stderr,
3205 "Bad iosw from %s() #%d\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n%s\n",
3207 1, 0, nbytes*nstrides,
3211 fmt_ioreq(req, sy, fd),
3212 (*sy->sy_format)(req, sy, fd, addr));
3213 doio_upanic(U_IOSW);
3217 aio_unregister(s->aioid[i]);
3221 for( i=0; s->aioid[i] != -1; i++ ) {
3222 if(s->aioid == NULL) {
3223 doio_fprintf(stderr,
3224 "aioid == NULL!\n");
3227 aiop = aio_slot(s->aioid[i]);
3230 * make sure the io completed without error
3232 if (aiop->aio_errno != 0) {
3233 doio_fprintf(stderr,
3234 "%s() aio error set: %s (%d)\n%s\n%s\n",
3236 strerror(aiop->aio_errno),
3238 fmt_ioreq(req, sy, fd),
3239 (*sy->sy_format)(req, sy, fd, addr));
3240 doio_upanic(U_IOSW);
3242 } else if (aiop->aio_ret != nbytes) {
3243 doio_fprintf(stderr,
3244 "Bad aio return from %s() #%d\nExpected (%d,%d), got (%d,%d)\n%s\n%s\n",
3249 fmt_ioreq(req, sy, fd),
3250 (*sy->sy_format)(req, sy, fd, addr));
3251 aio_unregister(s->aioid[i]);
3252 doio_upanic(U_IOSW);
3255 aio_unregister(s->aioid[i]);
3262 if(s->rval != mem_needed) {
3263 doio_fprintf(stderr,
3264 "%s() request returned wrong # of bytes - expected %d, got %d\n%s\n%s\n",
3265 sy->sy_name, nbytes, s->rval,
3266 fmt_ioreq(req, sy, fd),
3267 (*sy->sy_format)(req, sy, fd, addr));
3269 doio_upanic(U_RVAL);
3276 * Verify that the data was written correctly - check_file() returns
3277 * a non-null pointer which contains an error message if there are
3281 if ( rval == 0 && sy->sy_flags & SY_WRITE && v_opt) {
3282 msg = check_file(file, offset, nbytes*nstrides*nents,
3283 Pattern, Pattern_Length, 0,
3284 oflags & O_PARALLEL);
3286 doio_fprintf(stderr, "%s\n%s\n%s\n",
3288 fmt_ioreq(req, sy, fd),
3289 (*sy->sy_format)(req, sy, fd, addr));
3290 doio_upanic(U_CORRUPTION);
3296 * General cleanup ...
3298 * Write extent information to the write-log, so that doio_check can do
3299 * corruption detection. Note that w_done is set to 1, indicating that
3300 * the write has been verified as complete. We don't need to write the
3301 * filename on the second logging.
3304 if (w_opt && logged_write) {
3306 wlog_record_write(&Wlog, &wrec, woffset);
3310 * Unlock file region if necessary
3314 if (lock_file_region(file, fd, F_UNLCK,
3315 min_byte, (max_byte-min_byte+1)) < 0) {
3321 if(s->aioid != NULL)
3324 return (rval == -1) ? -1 : 0;
3329 * xfsctl-based requests
3331 * - XFS_IOC_UNRESVSP
3338 int fd, oflags, offset, nbytes;
3341 int min_byte = 0, max_byte = 0;
3342 char *file, *msg = NULL;
3343 struct xfs_flock64 flk;
3346 * Initialize common fields - assumes r_oflags, r_file, r_offset, and
3347 * r_nbytes are at the same offset in the read_req and reada_req
3350 file = req->r_data.io.r_file;
3351 oflags = req->r_data.io.r_oflags;
3352 offset = req->r_data.io.r_offset;
3353 nbytes = req->r_data.io.r_nbytes;
3356 flk.l_whence=SEEK_SET;
3361 * Get an open file descriptor
3364 if ((fd = alloc_fd(file, oflags)) == -1)
3371 * Lock data if this is locking option is set
3375 max_byte = offset + nbytes;
3377 if (lock_file_region(file, fd, F_WRLCK,
3378 min_byte, (nbytes+1)) < 0) {
3379 doio_fprintf(stderr,
3380 "file lock failed:\n");
3381 doio_fprintf(stderr,
3382 " buffer(req, %d, 0, 0x%x, 0x%x)\n",
3383 offset, min_byte, max_byte);
3391 switch (req->r_type) {
3392 case RESVSP: op=XFS_IOC_RESVSP; msg="resvsp"; break;
3393 case UNRESVSP: op=XFS_IOC_UNRESVSP; msg="unresvsp"; break;
3396 rval = xfsctl(file, fd, op, &flk);
3399 doio_fprintf(stderr,
3400 "xfsctl %s request failed: %s (%d)\n\txfsctl(%d, %s %d, {%d %lld ==> %lld}\n",
3402 fd, msg, op, flk.l_whence,
3403 (long long)flk.l_start,
3404 (long long)flk.l_len);
3406 doio_upanic(U_RVAL);
3411 * Unlock file region if necessary
3415 if (lock_file_region(file, fd, F_UNLCK,
3416 min_byte, (max_byte-min_byte+1)) < 0) {
3422 return (rval == -1) ? -1 : 0;
3427 * fsync(2) and fdatasync(2)
3439 * Initialize common fields - assumes r_oflags, r_file, r_offset, and
3440 * r_nbytes are at the same offset in the read_req and reada_req
3443 file = req->r_data.io.r_file;
3444 oflags = req->r_data.io.r_oflags;
3447 * Get an open file descriptor
3450 if ((fd = alloc_fd(file, oflags)) == -1)
3454 switch(req->r_type) {
3459 rval = fdatasync(fd);
3464 return (rval == -1) ? -1 : 0;
3470 doio_pat_fill(char *addr, int mem_needed, char *Pattern, int Pattern_Length,
3473 return pattern_fill(addr, mem_needed, Pattern, Pattern_Length, 0);
3477 doio_pat_check(buf, offset, length, pattern, pattern_length, patshift)
3485 static char errbuf[4096];
3486 int nb, i, pattern_index;
3487 char *cp, *bufend, *ep;
3488 char actual[33], expected[33];
3490 if (pattern_check(buf, length, pattern, pattern_length, patshift) != 0) {
3492 ep += sprintf(ep, "Corrupt regions follow - unprintable chars are represented as '.'\n");
3493 ep += sprintf(ep, "-----------------------------------------------------------------\n");
3495 pattern_index = patshift % pattern_length;;
3497 bufend = buf + length;
3499 while (cp < bufend) {
3500 if (*cp != pattern[pattern_index]) {
3502 if (nb > sizeof(expected)-1) {
3503 nb = sizeof(expected)-1;
3506 ep += sprintf(ep, "corrupt bytes starting at file offset %d\n", offset + (int)(cp-buf));
3509 * Fill in the expected and actual patterns
3511 bzero(expected, sizeof(expected));
3512 bzero(actual, sizeof(actual));
3514 for (i = 0; i < nb; i++) {
3515 expected[i] = pattern[(pattern_index + i) % pattern_length];
3516 if (! isprint((int)expected[i])) {
3521 if (! isprint((int)actual[i])) {
3526 ep += sprintf(ep, " 1st %2d expected bytes: %s\n", nb, expected);
3527 ep += sprintf(ep, " 1st %2d actual bytes: %s\n", nb, actual);
3534 if (pattern_index == pattern_length) {
3547 * Check the contents of a file beginning at offset, for length bytes. It
3548 * is assumed that there is a string of pattern bytes in this area of the
3549 * file. Use normal buffered reads to do the verification.
3551 * If there is a data mismatch, write a detailed message into a static buffer
3552 * suitable for the caller to print. Otherwise print NULL.
3554 * The fsa flag is set to non-zero if the buffer should be read back through
3555 * the FSA (unicos/mk). This implies the file will be opened
3556 * O_PARALLEL|O_RAW|O_WELLFORMED to do the validation. We must do this because
3557 * FSA will not allow the file to be opened for buffered io if it was
3558 * previously opened for O_PARALLEL io.
3562 check_file(file, offset, length, pattern, pattern_length, patshift, fsa)
3571 static char errbuf[4096];
3573 char *buf, *em, *ep;
3575 struct fd_cache *fdc;
3581 flags = Validation_Flags | O_RDONLY;
3586 flags |= O_PARALLEL | O_RAW | O_WELLFORMED;
3591 if ((fd = alloc_fd(file, flags)) == -1) {
3593 "Could not open file %s with flags %#o (%s) for data comparison: %s (%d)\n",
3594 file, flags, format_oflags(flags),
3599 if (lseek(fd, offset, SEEK_SET) == -1) {
3601 "Could not lseek to offset %d in %s for verification: %s (%d)\n",
3602 offset, file, SYSERR, errno);
3607 /* Guarantee a properly aligned address on Direct I/O */
3608 fdc = alloc_fdcache(file, flags);
3609 if( (flags & O_DIRECT) && ((long)buf % fdc->c_memalign != 0) ) {
3610 buf += fdc->c_memalign - ((long)buf % fdc->c_memalign);
3614 if ((nb = read(fd, buf, length)) == -1) {
3617 "Could not read %d bytes from %s for verification: %s (%d)\n\tread(%d, 0x%p, %d)\n\tbuf %% alignment(%d) = %ld\n",
3618 length, file, SYSERR, errno,
3620 fdc->c_memalign, (long)buf % fdc->c_memalign);
3623 "Could not read %d bytes from %s for verification: %s (%d)\n",
3624 length, file, SYSERR, errno);
3632 "Read wrong # bytes from %s. Expected %d, got %d\n",
3637 if( (em = (*Data_Check)(buf, offset, length, pattern, pattern_length, patshift)) != NULL ) {
3639 ep += sprintf(ep, "*** DATA COMPARISON ERROR ***\n");
3640 ep += sprintf(ep, "check_file(%s, %d, %d, %s, %d, %d) failed\n\n",
3641 file, offset, length, pattern, pattern_length, patshift);
3642 ep += sprintf(ep, "Comparison fd is %d, with open flags %#o\n",
3651 * Function to single-thread stdio output.
3655 doio_fprintf(FILE *stream, char *format, ...)
3657 static int pid = -1;
3663 date = hms(time(0));
3669 flk.l_whence = flk.l_start = flk.l_len = 0;
3670 flk.l_type = F_WRLCK;
3671 fcntl(fileno(stream), F_SETLKW, &flk);
3673 va_start(arglist, format);
3674 rval = fprintf(stream, "\n%s%s (%5d) %s\n", Prog, TagName, pid, date);
3675 rval += fprintf(stream, "---------------------\n");
3676 vfprintf(stream, format, arglist);
3681 flk.l_type = F_UNLCK;
3682 fcntl(fileno(stream), F_SETLKW, &flk);
3688 * Simple function for allocating core memory. Uses Memsize and Memptr to
3689 * keep track of the current amount allocated.
3698 int me = 0, flags, key, shmid;
3699 static int mturn = 0; /* which memory type to use */
3703 struct shmid_ds shm_ds;
3707 bzero( &shm_ds, sizeof(struct shmid_ds) );
3710 /* nbytes = -1 means "free all allocated memory" */
3711 if( nbytes == -1 ) {
3713 for(me=0; me < Nmemalloc; me++) {
3714 if(Memalloc[me].space == NULL)
3717 switch(Memalloc[me].memtype) {
3720 if(Memalloc[me].flags & MEMF_MPIN)
3721 munpin(Memalloc[me].space,
3724 free(Memalloc[me].space);
3725 Memalloc[me].space = NULL;
3731 if(Memalloc[me].flags & MEMF_MPIN)
3732 munpin(Memalloc[me].space,
3735 shmdt(Memalloc[me].space);
3736 Memalloc[me].space = NULL;
3738 shmctl(Memalloc[me].fd, IPC_RMID);
3740 shmctl(Memalloc[me].fd, IPC_RMID, &shm_ds);
3745 if(Memalloc[me].flags & MEMF_MPIN)
3746 munpin(Memalloc[me].space,
3749 munmap(Memalloc[me].space,
3751 close(Memalloc[me].fd);
3752 if(Memalloc[me].flags & MEMF_FILE) {
3753 unlink(Memalloc[me].name);
3755 Memalloc[me].space = NULL;
3758 doio_fprintf(stderr, "alloc_mem: HELP! Unknown memory space type %d index %d\n",
3759 Memalloc[me].memtype, me);
3767 * Select a memory area (currently round-robbin)
3770 if(mturn >= Nmemalloc)
3773 M = &Memalloc[mturn];
3775 switch(M->memtype) {
3777 if( nbytes > M->size ) {
3778 if( M->space != NULL ){
3780 if( M->flags & MEMF_MPIN )
3781 munpin( M->space, M->size );
3789 if( M->space == NULL ) {
3790 if( (cp = malloc( nbytes )) == NULL ) {
3791 doio_fprintf(stderr, "malloc(%d) failed: %s (%d)\n",
3792 nbytes, SYSERR, errno);
3796 if(M->flags & MEMF_MPIN) {
3797 if( mpin(cp, nbytes) == -1 ) {
3798 doio_fprintf(stderr, "mpin(0x%lx, %d) failed: %s (%d)\n",
3799 cp, nbytes, SYSERR, errno);
3803 M->space = (void *)cp;
3809 if( nbytes > M->size ) {
3810 if( M->space != NULL ) {
3812 if( M->flags & MEMF_MPIN )
3813 munpin(M->space, M->size);
3815 munmap(M->space, M->size);
3817 if( M->flags & MEMF_FILE )
3824 if( M->space == NULL ) {
3825 if(strchr(M->name, '%')) {
3826 sprintf(filename, M->name, getpid());
3827 M->name = strdup(filename);
3830 if( (M->fd = open(M->name, O_CREAT|O_RDWR, 0666)) == -1) {
3831 doio_fprintf(stderr, "alloc_mmap: error %d (%s) opening '%s'\n",
3839 M->size = nbytes * 4;
3841 /* bias addr if MEMF_ADDR | MEMF_FIXADDR */
3842 /* >>> how to pick a memory address? */
3844 /* bias flags on MEMF_PRIVATE etc */
3845 if(M->flags & MEMF_PRIVATE)
3846 flags |= MAP_PRIVATE;
3848 if(M->flags & MEMF_LOCAL)
3850 if(M->flags & MEMF_AUTORESRV)
3851 flags |= MAP_AUTORESRV;
3852 if(M->flags & MEMF_AUTOGROW)
3853 flags |= MAP_AUTOGROW;
3855 if(M->flags & MEMF_SHARED)
3856 flags |= MAP_SHARED;
3858 /*printf("alloc_mem, about to mmap, fd=%d, name=(%s)\n", M->fd, M->name);*/
3859 if( (M->space = mmap(addr, M->size,
3860 PROT_READ|PROT_WRITE,
3863 doio_fprintf(stderr, "alloc_mem: mmap error. errno %d (%s)\n\tmmap(addr 0x%x, size %d, read|write 0x%x, mmap flags 0x%x [%#o], fd %d, 0)\n\tfile %s\n",
3866 PROT_READ|PROT_WRITE,
3867 flags, M->flags, M->fd,
3869 doio_fprintf(stderr, "\t%s%s%s%s%s",
3870 (flags & MAP_PRIVATE) ? "private " : "",
3872 (flags & MAP_LOCAL) ? "local " : "",
3873 (flags & MAP_AUTORESRV) ? "autoresrv " : "",
3874 (flags & MAP_AUTOGROW) ? "autogrow " : "",
3876 (flags & MAP_SHARED) ? "shared" : "");
3883 if( nbytes > M->size ) {
3884 if( M->space != NULL ) {
3886 if( M->flags & MEMF_MPIN )
3887 munpin(M->space, M->size);
3891 shmctl( M->fd, IPC_RMID );
3893 shmctl( M->fd, IPC_RMID, &shm_ds );
3900 if(M->space == NULL) {
3901 if(!strcmp(M->name, "private")) {
3904 sscanf(M->name, "%i", &key);
3907 M->size = M->nblks ? M->nblks * 512 : nbytes;
3909 if( nbytes > M->size ){
3911 doio_fprintf(stderr, "MEM_SHMEM: nblks(%d) too small: nbytes=%d Msize=%d, skipping this req.\n",
3912 M->nblks, nbytes, M->size );
3917 shmid = shmget(key, M->size, IPC_CREAT|0666);
3919 doio_fprintf(stderr, "shmget(0x%x, %d, CREAT) failed: %s (%d)\n",
3920 key, M->size, SYSERR, errno);
3924 M->space = shmat(shmid, NULL, SHM_RND);
3925 if( M->space == (void *)-1 ) {
3926 doio_fprintf(stderr, "shmat(0x%x, NULL, SHM_RND) failed: %s (%d)\n",
3927 shmid, SYSERR, errno);
3931 if(M->flags & MEMF_MPIN) {
3932 if( mpin(M->space, M->size) == -1 ) {
3933 doio_fprintf(stderr, "mpin(0x%lx, %d) failed: %s (%d)\n",
3934 M->space, M->size, SYSERR, errno);
3942 doio_fprintf(stderr, "alloc_mem: HELP! Unknown memory space type %d index %d\n",
3943 Memalloc[me].memtype, mturn);
3962 static char *malloc_space;
3965 * The "unicos" version of this did some stuff with sbrk;
3966 * this caused problems with async I/O on irix, and now appears
3967 * to be causing problems with FSA I/O on unicos/mk.
3970 if (nbytes > Memsize) {
3971 if ((cp = (char *)sbrk(nbytes - Memsize)) == (char *)-1) {
3972 doio_fprintf(stderr, "sbrk(%d) failed: %s (%d)\n",
3973 nbytes - Memsize, SYSERR, errno);
3979 Memsize += nbytes - Memsize;
3983 /* nbytes = -1 means "free all allocated memory" */
3984 if( nbytes == -1 ) {
3985 free( malloc_space );
3991 if( nbytes > Memsize ) {
3993 free( malloc_space );
3995 if( (cp = malloc_space = malloc( nbytes )) == NULL ) {
3996 doio_fprintf(stderr, "malloc(%d) failed: %s (%d)\n",
3997 nbytes, SYSERR, errno);
4002 /* T3E requires memory to be aligned on 0x40 word boundaries */
4004 if( ip & 0x3F != 0 ) {
4005 doio_fprintf(stderr, "malloc(%d) = 0x%x(0x%x) not aligned by 0x%x\n",
4006 nbytes, cp, ip, ip & 0x3f);
4009 if( (cp = malloc_space = malloc( nbytes + 0x40 )) == NULL ) {
4010 doio_fprintf(stderr, "malloc(%d) failed: %s (%d)\n",
4011 nbytes, SYSERR, errno);
4015 cp += (0x40 - (ip & 0x3F));
4017 #endif /* _CRAYT3E */
4027 * Simple function for allocating sds space. Uses Sdssize and Sdsptr to
4028 * keep track of location and size of currently allocated chunk.
4039 if (nbytes > Sdssize) {
4040 if ((nblks = ssbreak(btoc(nbytes - Sdssize))) == -1) {
4041 doio_fprintf(stderr, "ssbreak(%d) failed: %s (%d)\n",
4042 btoc(nbytes - Sdssize), SYSERR, errno);
4046 Sdssize = ctob(nblks);
4061 doio_fprintf(stderr,
4062 "Internal Error - alloc_sds() called on a CRAY2 system\n");
4072 * Function to maintain a file descriptor cache, so that doio does not have
4073 * to do so many open() and close() calls. Descriptors are stored in the
4074 * cache by file name, and open flags. Each entry also has a _rtc value
4075 * associated with it which is used in aging. If doio cannot open a file
4076 * because it already has too many open (ie. system limit hit) it will close
4077 * the one in the cache that has the oldest _rtc value.
4079 * If alloc_fd() is called with a file of NULL, it will close all descriptors
4080 * in the cache, and free the memory in the cache.
4084 alloc_fd(file, oflags)
4088 struct fd_cache *fdc;
4089 struct fd_cache *alloc_fdcache(char *file, int oflags);
4091 fdc = alloc_fdcache(file, oflags);
4099 alloc_fdcache(file, oflags)
4104 struct fd_cache *free_slot, *oldest_slot, *cp;
4105 static int cache_size = 0;
4106 static struct fd_cache *cache = NULL;
4108 struct dioattr finfo;
4112 * If file is NULL, it means to free up the fd cache.
4115 if (file == NULL && cache != NULL) {
4116 for (cp = cache; cp < &cache[cache_size]; cp++) {
4117 if (cp->c_fd != -1) {
4121 if (cp->c_memaddr != NULL) {
4122 munmap(cp->c_memaddr, cp->c_memlen);
4137 * Look for a fd in the cache. If one is found, return it directly.
4138 * Otherwise, when this loop exits, oldest_slot will point to the
4139 * oldest fd slot in the cache, and free_slot will point to an
4140 * unoccupied slot if there are any.
4143 for (cp = cache; cp != NULL && cp < &cache[cache_size]; cp++) {
4144 if (cp->c_fd != -1 &&
4145 cp->c_oflags == oflags &&
4146 strcmp(cp->c_file, file) == 0) {
4155 if (cp->c_fd == -1) {
4156 if (free_slot == NULL) {
4160 if (oldest_slot == NULL ||
4161 cp->c_rtc < oldest_slot->c_rtc) {
4168 * No matching file/oflags pair was found in the cache. Attempt to
4172 if ((fd = open(file, oflags, 0666)) < 0) {
4173 if (errno != EMFILE) {
4174 doio_fprintf(stderr,
4175 "Could not open file %s with flags %#o (%s): %s (%d)\n",
4176 file, oflags, format_oflags(oflags),
4183 * If we get here, we have as many open fd's as we can have.
4184 * Close the oldest one in the cache (pointed to by
4185 * oldest_slot), and attempt to re-open.
4188 close(oldest_slot->c_fd);
4189 oldest_slot->c_fd = -1;
4190 free_slot = oldest_slot;
4192 if ((fd = open(file, oflags, 0666)) < 0) {
4193 doio_fprintf(stderr,
4194 "Could not open file %s with flags %#o (%s): %s (%d)\n",
4195 file, oflags, format_oflags(oflags),
4202 /*printf("alloc_fd: new file %s flags %#o fd %d\n", file, oflags, fd);*/
4205 * If we get here, fd is our open descriptor. If free_slot is NULL,
4206 * we need to grow the cache, otherwise free_slot is the slot that
4207 * should hold the fd info.
4210 if (free_slot == NULL) {
4211 cache = (struct fd_cache *)realloc(cache, sizeof(struct fd_cache) * (FD_ALLOC_INCR + cache_size));
4212 if (cache == NULL) {
4213 doio_fprintf(stderr, "Could not malloc() space for fd chace");
4218 cache_size += FD_ALLOC_INCR;
4220 for (cp = &cache[cache_size-FD_ALLOC_INCR];
4221 cp < &cache[cache_size]; cp++) {
4225 free_slot = &cache[cache_size - FD_ALLOC_INCR];
4229 * finally, fill in the cache slot info
4232 free_slot->c_fd = fd;
4233 free_slot->c_oflags = oflags;
4234 strcpy(free_slot->c_file, file);
4236 free_slot->c_rtc = _rtc();
4238 free_slot->c_rtc = Reqno;
4242 if (oflags & O_DIRECT) {
4243 if (xfsctl(file, fd, XFS_IOC_DIOINFO, &finfo) == -1) {
4245 finfo.d_miniosz = 1;
4246 finfo.d_maxiosz = 1;
4250 finfo.d_miniosz = 1;
4251 finfo.d_maxiosz = 1;
4254 free_slot->c_memalign = finfo.d_mem;
4255 free_slot->c_miniosz = finfo.d_miniosz;
4256 free_slot->c_maxiosz = finfo.d_maxiosz;
4259 free_slot->c_memaddr = NULL;
4260 free_slot->c_memlen = 0;
4268 * Signal Handling Section
4275 * "caller-id" for signals
4278 signal_info(int sig, siginfo_t *info, void *v)
4283 switch(info->si_code) {
4285 doio_fprintf(stderr,
4286 "signal_info: si_signo %d si_errno %d si_code SI_USER pid %d uid %d\n",
4287 info->si_signo, info->si_errno,
4288 info->si_pid, info->si_uid);
4293 doio_fprintf(stderr, "signal_info si_signo %d si_code = SI_QUEUE\n",
4300 if( (info->si_signo == SIGSEGV) ||
4301 (info->si_signo == SIGBUS) ){
4302 doio_fprintf(stderr, "signal_info si_signo %d si_errno %d si_code = %d si_addr=%p active_mmap_rw=%d havesigint=%d\n",
4303 info->si_signo, info->si_errno,
4304 info->si_code, info->si_addr,
4312 doio_fprintf(stderr, "signal_info: si_signo %d si_errno %d unknown code %d\n",
4313 info->si_signo, info->si_errno,
4317 doio_fprintf(stderr, "signal_info: sig %d\n", sig);
4324 cleanup_handler(int sig, siginfo_t *info, void *v)
4326 havesigint=1; /* in case there's a followup signal */
4327 /*signal_info(sig, info, v);*/ /* be quiet on "normal" kill */
4334 die_handler(int sig, siginfo_t *info, void *v)
4336 doio_fprintf(stderr, "terminating on signal %d\n", sig);
4337 signal_info(sig, info, v);
4343 sigbus_handler(int sig, siginfo_t *info, void *v)
4345 /* While we are doing a memcpy to/from an mmapped region we can
4346 get a SIGBUS for a variety of reasons--and not all of them
4347 should be considered failures.
4349 Under normal conditions if we get a SIGINT it means we've been
4350 told to shutdown. However, if we're currently doing the above-
4351 mentioned memcopy then the kernel will follow that SIGINT with
4352 a SIGBUS. We can guess that we're in this situation by seeing
4353 that the si_errno field in the siginfo structure has EINTR as
4354 an errno. (We might make the guess stronger by looking at the
4355 si_addr field to see that it's not faulting off the end of the
4356 mmapped region, but it seems that in such a case havesigint
4357 would not have been set so maybe that doesn't make the guess
4362 if( active_mmap_rw && havesigint && (info->si_errno == EINTR) ){
4363 cleanup_handler( sig, info, v );
4366 die_handler( sig, info, v );
4374 havesigint=1; /* in case there's a followup signal */
4383 doio_fprintf(stderr, "terminating on signal %d\n", sig);
4393 /* See sigbus_handler() in the 'ifdef sgi' case for details. Here,
4394 we don't have the siginfo stuff so the guess is weaker but we'll
4398 if( active_mmap_rw && havesigint )
4416 * SIGINT handler for the parent (original doio) process. It simply sends
4417 * a SIGINT to all of the doio children. Since they're all in the same
4418 * pgrp, this can be done with a single kill().
4426 for (i = 0; i < Nchildren; i++) {
4427 if (Children[i] != -1) {
4428 kill(Children[i], SIGINT);
4434 * Signal handler used to inform a process when async io completes. Referenced
4435 * in do_read() and do_write(). Note that the signal handler is not
4444 struct aio_info *aiop;
4446 for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) {
4447 aiop = &Aio_Info[i];
4449 if (aiop->strategy == A_SIGNAL && aiop->sig == sig) {
4452 if (aio_done(aiop)) {
4460 * dump info on all open aio slots
4468 for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) {
4469 if (Aio_Info[i].busy) {
4472 "Aio_Info[%03d] id=%d fd=%d signal=%d signaled=%d\n",
4476 Aio_Info[i].signalled);
4477 fprintf(stderr, "\tstrategy=%s\n",
4478 format_strat(Aio_Info[i].strategy));
4481 fprintf(stderr, "%d active async i/os\n", count);
4487 * Signal handler called as a callback, not as a signal.
4488 * 'val' is the value from sigev_value and is assumed to be the
4495 struct aio_info *aiop;
4497 /*printf("cb_handler requesting slot %d\n", val.sival_int);*/
4498 aiop = aio_slot( val.sival_int );
4499 /*printf("cb_handler, aiop=%p\n", aiop);*/
4501 /*printf("%d in cb_handler\n", getpid() );*/
4502 if (aiop->strategy == A_CALLBACK) {
4505 if (aio_done(aiop)) {
4518 struct aio_info *aiop;
4522 for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) {
4524 if (! Aio_Info[i].busy) {
4525 aiop = &Aio_Info[i];
4531 if (Aio_Info[i].busy && Aio_Info[i].id == aio_id) {
4532 aiop = &Aio_Info[i];
4539 doio_fprintf(stderr,"aio_slot(%d) not found. Request %d\n",
4550 aio_register(fd, strategy, sig)
4555 struct aio_info *aiop;
4557 struct sigaction sa;
4559 aiop = aio_slot(-1);
4562 aiop->strategy = strategy;
4565 bzero((char *)&aiop->iosw, sizeof(aiop->iosw));
4568 if (strategy == A_SIGNAL) {
4570 aiop->signalled = 0;
4572 sa.sa_handler = aio_handler;
4574 sigemptyset(&sa.sa_mask);
4576 sigaction(sig, &sa, &aiop->osa);
4579 aiop->signalled = 0;
4586 aio_unregister(aio_id)
4589 struct aio_info *aiop;
4591 aiop = aio_slot(aio_id);
4593 if (aiop->strategy == A_SIGNAL) {
4594 sigaction(aiop->sig, &aiop->osa, NULL);
4606 #ifdef RECALL_SIZEOF
4607 long mask[RECALL_SIZEOF];
4610 struct aio_info *aiop;
4612 struct iosw *ioswlist[1];
4615 const aiocb_t *aioary[1];
4620 aiop = aio_slot(aio_id);
4621 /*printf("%d aiop B =%p\n", getpid(), aiop);*/
4623 switch (aiop->strategy) {
4625 while (! aio_done(aiop))
4630 sigemptyset(&sigset);
4631 sighold( aiop->sig );
4633 while ( !aiop->signalled || !aiop->done ) {
4634 sigsuspend(&sigset);
4635 sighold( aiop->sig );
4641 ioswlist[0] = &aiop->iosw;
4642 if (recall(aiop->fd, 1, ioswlist) < 0) {
4643 doio_fprintf(stderr, "recall() failed: %s (%d)\n",
4649 #ifdef RECALL_SIZEOF
4653 RECALL_SET(mask, aiop->fd);
4654 if (recalla(mask) < 0) {
4655 doio_fprintf(stderr, "recalla() failed: %s (%d)\n",
4660 RECALL_CLR(mask, aiop->fd);
4665 ioswlist[0] = &aiop->iosw;
4666 if (recalls(1, ioswlist) < 0) {
4667 doio_fprintf(stderr, "recalls failed: %s (%d)\n",
4676 aioary[0] = &aiop->aiocb;
4679 r = aio_suspend(aioary, 1, NULL);
4681 doio_fprintf(stderr, "aio_suspend failed: %s (%d)\n",
4686 } while(aiop->done == 0);
4690 * after having this set for a while, I've decided that
4694 doio_fprintf(stderr, "aio_wait: callback wait took %d tries\n", cnt);
4698 * Note: cb_handler already calls aio_done
4704 aioary[0] = &aiop->aiocb;
4705 r = aio_suspend(aioary, 1, NULL);
4707 doio_fprintf(stderr, "aio_suspend failed: %s (%d)\n",
4717 /*printf("aio_wait: errno %d return %d\n", aiop->aio_errno, aiop->aio_ret);*/
4724 * Format specified time into HH:MM:SS format. t is the time to format
4725 * in seconds (as returned from time(2)).
4732 static char ascii_time[9];
4735 ltime = localtime(&t);
4736 strftime(ascii_time, sizeof(ascii_time), "%H:%M:%S", ltime);
4742 * Simple routine to check if an async io request has completed.
4746 aio_done(struct aio_info *ainfo)
4749 return ainfo->iosw.sw_flag;
4753 if( (ainfo->aio_errno = aio_error(&ainfo->aiocb)) == -1 ){
4754 doio_fprintf(stderr, "aio_done: aio_error failed: %s (%d)\n",
4758 /*printf("%d aio_done aio_errno=%d\n", getpid(), ainfo->aio_errno);*/
4759 if( ainfo->aio_errno != EINPROGRESS ){
4760 if( (ainfo->aio_ret = aio_return(&ainfo->aiocb)) == -1 ){
4761 doio_fprintf(stderr, "aio_done: aio_return failed: %s (%d)\n",
4767 return (ainfo->aio_errno != EINPROGRESS);
4769 return -1; /* invalid */
4774 * Routine to handle upanic() - it first attempts to set the panic flag. If
4775 * the flag cannot be set, an error message is issued. A call to upanic
4776 * with PA_PANIC is then done unconditionally, in case the panic flag was set
4777 * from outside the program (as with the panic(8) program).
4779 * Note - we only execute the upanic code if -U was used, and the passed in
4780 * mask is set in the Upanic_Conditions bitmask.
4787 if (U_opt == 0 || (mask & Upanic_Conditions) == 0) {
4792 if (upanic(PA_SET) < 0) {
4793 doio_fprintf(stderr, "WARNING - Could not set the panic flag - upanic(PA_SET) failed: %s (%d)\n",
4800 syssgi(1005); /* syssgi test panic - DEBUG kernels only */
4802 doio_fprintf(stderr, "WARNING - upanic() failed\n");
4806 * Parse cmdline options/arguments and set appropriate global variables.
4807 * If the cmdline is valid, return 0 to caller. Otherwise exit with a status
4812 parse_cmdline(argc, argv, opts)
4818 char cc, *cp, *tok = NULL;
4821 extern char *optarg;
4823 char *memargs[NMEMALLOC];
4825 void parse_memalloc(char *arg);
4826 void parse_delay(char *arg);
4827 void dump_memalloc();
4829 if (*argv[0] == '-') {
4834 if ((Prog = strrchr(argv[0], '/')) == NULL) {
4841 while ((c = getopt(argc, argv, opts)) != EOF) {
4849 for(s=checkmap; s->string != NULL; s++)
4850 if(!strcmp(s->string, optarg))
4852 if (s->string == NULL) {
4854 "%s%s: Illegal -C arg (%s). Must be one of: ",
4855 Prog, TagName, tok);
4857 for (s = checkmap; s->string != NULL; s++)
4858 fprintf(stderr, "%s ", s->string);
4859 fprintf(stderr, "\n");
4865 Data_Fill = doio_pat_fill;
4866 Data_Check = doio_pat_check;
4870 "%s%s: Unrecognised -C arg '%s' %d",
4871 Prog, TagName, s->string, s->value);
4876 case 'd': /* delay between i/o ops */
4877 parse_delay(optarg);
4881 if (Npes > 1 && Nprocs > 1) {
4882 fprintf(stderr, "%s%s: Warning - Program is a multi-pe application - exec option is ignored.\n", Prog, TagName);
4897 Message_Interval = strtol(optarg, &cp, 10);
4898 if (*cp != '\0' || Message_Interval < 0) {
4899 fprintf(stderr, "%s%s: Illegal -m arg (%s): Must be an integer >= 0\n", Prog, TagName, optarg);
4905 case 'M': /* memory allocation types */
4907 nmemargs = string_to_tokens(optarg, memargs, 32, ",");
4908 for(ma=0; ma < nmemargs; ma++) {
4909 parse_memalloc(memargs[ma]);
4911 /*dump_memalloc();*/
4913 fprintf(stderr, "%s%s: Error: -M isn't supported on this platform\n", Prog, TagName);
4920 sprintf( TagName, "(%.39s)", optarg );
4924 Nprocs = strtol(optarg, &cp, 10);
4925 if (*cp != '\0' || Nprocs < 1) {
4927 "%s%s: Illegal -n arg (%s): Must be integer > 0\n",
4928 Prog, TagName, optarg);
4932 if (Npes > 1 && Nprocs > 1) {
4933 fprintf(stderr, "%s%s: Program has been built as a multi-pe app. -n1 is the only nprocs value allowed\n", Prog, TagName);
4940 Release_Interval = strtol(optarg, &cp, 10);
4941 if (*cp != '\0' || Release_Interval < 0) {
4943 "%s%s: Illegal -r arg (%s): Must be integer >= 0\n",
4944 Prog, TagName, optarg);
4961 if (strcasecmp(optarg, "sync") == 0) {
4962 Validation_Flags = O_SYNC;
4963 } else if (strcasecmp(optarg, "buffered") == 0) {
4964 Validation_Flags = 0;
4966 } else if (strcasecmp(optarg, "parallel") == 0) {
4967 Validation_Flags = O_PARALLEL;
4968 } else if (strcasecmp(optarg, "ldraw") == 0) {
4969 Validation_Flags = O_LDRAW;
4970 } else if (strcasecmp(optarg, "raw") == 0) {
4971 Validation_Flags = O_RAW;
4973 } else if (strcasecmp(optarg, "direct") == 0) {
4974 Validation_Flags = O_DIRECT;
4976 if (sscanf(optarg, "%i%c", &Validation_Flags, &cc) != 1) {
4977 fprintf(stderr, "%s: Invalid -V argument (%s) - must be a decimal, hex, or octal\n", Prog, optarg);
4978 fprintf(stderr, " number, or one of the following strings: 'sync',\n");
4979 fprintf(stderr, " 'buffered', 'parallel', 'ldraw', or 'raw'\n");
4986 tok = strtok(optarg, ",");
4987 while (tok != NULL) {
4988 for (s = Upanic_Args; s->string != NULL; s++)
4989 if (strcmp(s->string, tok) == 0)
4992 if (s->string == NULL) {
4994 "%s%s: Illegal -U arg (%s). Must be one of: ",
4995 Prog, TagName, tok);
4997 for (s = Upanic_Args; s->string != NULL; s++)
4998 fprintf(stderr, "%s ", s->string);
5000 fprintf(stderr, "\n");
5005 Upanic_Conditions |= s->value;
5006 tok = strtok(NULL, ",");
5024 Data_Fill = doio_pat_fill;
5025 Data_Check = doio_pat_check;
5029 Upanic_Conditions = 0;
5035 Release_Interval = DEF_RELEASE_INTERVAL;
5038 Memalloc[Nmemalloc].memtype = MEM_DATA;
5039 Memalloc[Nmemalloc].flags = 0;
5040 Memalloc[Nmemalloc].name = NULL;
5041 Memalloc[Nmemalloc].space = NULL;
5046 * Initialize input stream
5049 if (argc == optind) {
5052 Infile = argv[optind++];
5055 if (argc != optind) {
5066 * Parse memory allocation types
5070 * T3E-shmem:blksize[:nblks]
5071 * SysV-shmem:shmid:blksize:nblks
5072 * if shmid is "private", use IPC_PRIVATE
5073 * and nblks is not required
5075 * mmap:flags:filename:blksize[:nblks]
5077 * p - private (MAP_PRIVATE)
5078 * a - private, MAP_AUTORESRV
5079 * l - local (MAP_LOCAL)
5080 * s - shared (nblks required)
5083 * f - fixed address (MAP_FIXED)
5084 * A - use an address without MAP_FIXED
5085 * a - autogrow (map once at startup)
5087 * mmap:flags:devzero
5088 * mmap /dev/zero (shared not allowd)
5089 * maps the first 4096 bytes of /dev/zero
5091 * - put a directory at the beginning of the shared
5092 * regions saying what pid has what region.
5096 * nblks worth of directories - 1 int pids
5100 parse_memalloc(char *arg)
5102 char *allocargs[NMEMALLOC];
5106 if(Nmemalloc >= NMEMALLOC) {
5107 doio_fprintf(stderr, "Error - too many memory types (%d).\n",
5112 M = &Memalloc[Nmemalloc];
5114 nalloc = string_to_tokens(arg, allocargs, 32, ":");
5115 if(!strcmp(allocargs[0], "data")) {
5116 M->memtype = MEM_DATA;
5122 if(strchr(allocargs[1], 'p'))
5123 M->flags |= MEMF_MPIN;
5125 } else if(!strcmp(allocargs[0], "mmap")) {
5126 /* mmap:flags:filename[:size] */
5127 M->memtype = MEM_MMAP;
5131 if(strchr(allocargs[1], 'p'))
5132 M->flags |= MEMF_PRIVATE;
5133 if(strchr(allocargs[1], 'a'))
5134 M->flags |= MEMF_AUTORESRV;
5135 if(strchr(allocargs[1], 'l'))
5136 M->flags |= MEMF_LOCAL;
5137 if(strchr(allocargs[1], 's'))
5138 M->flags |= MEMF_SHARED;
5140 if(strchr(allocargs[1], 'f'))
5141 M->flags |= MEMF_FIXADDR;
5142 if(strchr(allocargs[1], 'A'))
5143 M->flags |= MEMF_ADDR;
5144 if(strchr(allocargs[1], 'G'))
5145 M->flags |= MEMF_AUTOGROW;
5147 if(strchr(allocargs[1], 'U'))
5148 M->flags |= MEMF_FILE;
5150 M->flags |= MEMF_PRIVATE;
5154 if(!strcmp(allocargs[2], "devzero")) {
5155 M->name = "/dev/zero";
5157 ((MEMF_PRIVATE|MEMF_LOCAL) == 0))
5158 M->flags |= MEMF_PRIVATE;
5160 M->name = allocargs[2];
5163 M->name = "/dev/zero";
5165 ((MEMF_PRIVATE|MEMF_LOCAL) == 0))
5166 M->flags |= MEMF_PRIVATE;
5170 } else if(!strcmp(allocargs[0], "shmem")) {
5171 /* shmem:shmid:size */
5172 M->memtype = MEM_SHMEM;
5176 M->name = allocargs[1];
5181 sscanf(allocargs[2], "%i", &M->nblks);
5186 if(strchr(allocargs[3], 'p'))
5187 M->flags |= MEMF_MPIN;
5192 doio_fprintf(stderr, "Error - unknown memory type '%s'.\n",
5204 if(Nmemalloc == 0) {
5205 printf("No memory allocation strategies devined\n");
5209 for(ma=0; ma < Nmemalloc; ma++) {
5210 switch(Memalloc[ma].memtype) {
5211 case MEM_DATA: mt = "data"; break;
5212 case MEM_SHMEM: mt = "shmem"; break;
5213 case MEM_MMAP: mt = "mmap"; break;
5214 default: mt = "unknown"; break;
5216 printf("mstrat[%d] = %d %s\n", ma, Memalloc[ma].memtype, mt);
5217 printf("\tflags=%#o name='%s' nblks=%d\n",
5220 Memalloc[ma].nblks);
5227 * -d <op>:<time> - doio inter-operation delay
5228 * currently this permits ONE type of delay between operations.
5232 parse_delay(char *arg)
5234 char *delayargs[NMEMALLOC];
5238 ndelay = string_to_tokens(arg, delayargs, 32, ":");
5240 doio_fprintf(stderr,
5241 "Illegal delay arg (%s). Must be operation:time\n", arg);
5244 for(s=delaymap; s->string != NULL; s++)
5245 if(!strcmp(s->string, delayargs[0]))
5247 if (s->string == NULL) {
5249 "Illegal Delay arg (%s). Must be one of: ", arg);
5251 for (s = delaymap; s->string != NULL; s++)
5252 fprintf(stderr, "%s ", s->string);
5253 fprintf(stderr, "\n");
5259 sscanf(delayargs[1], "%i", &delaytime);
5263 "Warning: extra delay arguments ignored.\n");
5269 * Usage clause - obvious
5277 * Only do this if we are on vpe 0, to avoid seeing it from every
5278 * process in the application.
5281 if (Npes > 1 && Vpe != 0) {
5285 fprintf(stream, "usage%s: %s [-aekv] [-m message_interval] [-n nprocs] [-r release_interval] [-w write_log] [-V validation_ftype] [-U upanic_cond] [infile]\n", TagName, Prog);
5294 * Only the app running on vpe 0 gets to issue help - this prevents
5295 * everybody in the application from doing this.
5298 if (Npes > 1 && Vpe != 0) {
5303 fprintf(stream, "\n");
5304 fprintf(stream, "\t-a abort - kill all doio processes on data compare\n");
5305 fprintf(stream, "\t errors. Normally only the erroring process exits\n");
5306 fprintf(stream, "\t-C data-pattern-type \n");
5307 fprintf(stream, "\t Available data patterns are:\n");
5308 fprintf(stream, "\t default - repeating pattern\n");
5309 fprintf(stream, "\t-d Operation:Time Inter-operation delay.\n");
5310 fprintf(stream, "\t Operations are:\n");
5311 fprintf(stream, "\t select:time (1 second=1000000)\n");
5312 fprintf(stream, "\t sleep:time (1 second=1)\n");
5314 fprintf(stream, "\t sginap:time (1 second=CLK_TCK=100)\n");
5316 fprintf(stream, "\t alarm:time (1 second=1)\n");
5317 fprintf(stream, "\t-e Re-exec children before entering the main\n");
5318 fprintf(stream, "\t loop. This is useful for spreading\n");
5319 fprintf(stream, "\t procs around on multi-pe systems.\n");
5320 fprintf(stream, "\t-k Lock file regions during writes using fcntl()\n");
5321 fprintf(stream, "\t-v Verify writes - this is done by doing a buffered\n");
5322 fprintf(stream, "\t read() of the data if file io was done, or\n");
5323 fprintf(stream, "\t an ssread()of the data if sds io was done\n");
5325 fprintf(stream, "\t-M Data buffer allocation method\n");
5326 fprintf(stream, "\t alloc-type[,type]\n");
5328 fprintf(stream, "\t data:flags\n");
5329 fprintf(stream, "\t p - mpin buffer\n");
5330 fprintf(stream, "\t shmem:shmid:size:flags\n");
5331 fprintf(stream, "\t p - mpin buffer\n");
5333 fprintf(stream, "\t data\n");
5334 fprintf(stream, "\t shmem:shmid:size\n");
5336 fprintf(stream, "\t mmap:flags:filename\n");
5337 fprintf(stream, "\t p - private\n");
5339 fprintf(stream, "\t s - shared\n");
5340 fprintf(stream, "\t l - local\n");
5341 fprintf(stream, "\t a - autoresrv\n");
5342 fprintf(stream, "\t G - autogrow\n");
5344 fprintf(stream, "\t s - shared (shared file must exist\n"),
5345 fprintf(stream, "\t and have needed length)\n");
5347 fprintf(stream, "\t f - fixed address (not used)\n");
5348 fprintf(stream, "\t a - specify address (not used)\n");
5349 fprintf(stream, "\t U - Unlink file when done\n");
5350 fprintf(stream, "\t The default flag is private\n");
5351 fprintf(stream, "\n");
5353 fprintf(stream, "\t-m message_interval Generate a message every 'message_interval'\n");
5354 fprintf(stream, "\t requests. An interval of 0 suppresses\n");
5355 fprintf(stream, "\t messages. The default is 0.\n");
5356 fprintf(stream, "\t-N tagname Tag name, for Monster.\n");
5357 fprintf(stream, "\t-n nprocs # of processes to start up\n");
5358 fprintf(stream, "\t-r release_interval Release all memory and close\n");
5359 fprintf(stream, "\t files every release_interval operations.\n");
5360 fprintf(stream, "\t By default procs never release memory\n");
5361 fprintf(stream, "\t or close fds unless they have to.\n");
5362 fprintf(stream, "\t-V validation_ftype The type of file descriptor to use for doing data\n");
5363 fprintf(stream, "\t validation. validation_ftype may be an octal,\n");
5364 fprintf(stream, "\t hex, or decimal number representing the open()\n");
5365 fprintf(stream, "\t flags, or may be one of the following strings:\n");
5366 fprintf(stream, "\t 'buffered' - validate using bufferd read\n");
5367 fprintf(stream, "\t 'sync' - validate using O_SYNC read\n");
5368 fprintf(stream, "\t 'direct - validate using O_DIRECT read'\n");
5370 fprintf(stream, "\t 'ldraw' - validate using O_LDRAW read\n");
5371 fprintf(stream, "\t 'parallel' - validate using O_PARALLEL read\n");
5372 fprintf(stream, "\t 'raw' - validate using O_RAW read\n");
5374 fprintf(stream, "\t By default, 'parallel'\n");
5375 fprintf(stream, "\t is used if the write was done with O_PARALLEL\n");
5376 fprintf(stream, "\t or 'buffered' for all other writes.\n");
5377 fprintf(stream, "\t-w write_log File to log file writes to. The doio_check\n");
5378 fprintf(stream, "\t program can reconstruct datafiles using the\n");
5379 fprintf(stream, "\t write_log, and detect if a file is corrupt\n");
5380 fprintf(stream, "\t after all procs have exited.\n");
5381 fprintf(stream, "\t-U upanic_cond Comma separated list of conditions that will\n");
5382 fprintf(stream, "\t cause a call to upanic(PA_PANIC).\n");
5383 fprintf(stream, "\t 'corruption' -> upanic on bad data comparisons\n");
5384 fprintf(stream, "\t 'iosw' ---> upanic on unexpected async iosw\n");
5385 fprintf(stream, "\t 'rval' ---> upanic on unexpected syscall rvals\n");
5386 fprintf(stream, "\t 'all' ---> all of the above\n");
5387 fprintf(stream, "\n");
5388 fprintf(stream, "\tinfile Input stream - default is stdin - must be a list\n");
5389 fprintf(stream, "\t of io_req structures (see doio.h). Currently\n");
5390 fprintf(stream, "\t only the iogen program generates the proper\n");
5391 fprintf(stream, "\t format\n");