Whamcloud - gitweb
LU-17705 ptlrpc: replace synchronize_rcu() with rcu_barrier()
[fs/lustre-release.git] / lustre / tests / fsx.c
1 /*
2  * Copyright (C) 1991, NeXT Computer, Inc.  All Rights Reserverd.
3  * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved.
4  *
5  * Copyright (c) 2012, Intel Corporation.
6  *
7  * @APPLE_LICENSE_HEADER_START@
8  *
9  * The contents of this file constitute Original Code as defined in and
10  * are subject to the Apple Public Source License Version 1.1 (the
11  * "License").  You may not use this file except in compliance with the
12  * License.  Please obtain a copy of the License at
13  * http://www.apple.com/publicsource and read it before using this file.
14  *
15  * This Original Code and all software distributed under the License are
16  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
20  * License for the specific language governing rights and limitations
21  * under the License.
22  *
23  * @APPLE_LICENSE_HEADER_END@
24  *
25  *      File:   fsx.c
26  *      Author: Avadis Tevanian, Jr.
27  *
28  *      File system exerciser.
29  *
30  *      Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com
31  *
32  *      Various features from Joe Sokol, Pat Dirks, and Clark Warner.
33  *
34  *      Small changes to work under Linux -- davej.
35  *
36  *      Sundry porting patches from Guy Harris 12/2001
37  * $FreeBSD: src/tools/regression/fsx/fsx.c,v 1.1 2001/12/20 04:15:57 jkh Exp $
38  *
39  *      Checks for mmap last-page zero fill.
40  *
41  *      Add multi-file testing feature -- Zach Brown <zab@clusterfs.com>
42  *
43  *      Add random preallocation calls - Eric Sandeen <sandeen@redhat.com>
44  *
45  * $FreeBSD: src/tools/regression/fsx/fsx.c,v 1.2 2003/04/23 23:42:23 jkh Exp $
46  * $DragonFly: src/test/stress/fsx/fsx.c,v 1.2 2005/05/02 19:31:56 dillon Exp $
47  */
48 #ifndef _GNU_SOURCE
49 #define _GNU_SOURCE
50 #endif
51
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #if defined(_UWIN) || defined(__linux__)
55 # include <sys/param.h>
56 # include <limits.h>
57 # include <time.h>
58 # include <strings.h>
59 #endif
60 #include <sys/time.h>
61 #include <fcntl.h>
62 #include <sys/mman.h>
63 #ifndef MAP_FILE
64 # define MAP_FILE 0
65 #endif
66 #include <limits.h>
67 #include <signal.h>
68 #include <stdio.h>
69 #include <stddef.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <unistd.h>
73 #include <stdarg.h>
74 #include <errno.h>
75 #include <libcfs/util/string.h>
76 #include <setjmp.h>
77
78 /*
79  * Each test run will work with one or more separate file descriptors for the
80  * same file.  This allows testing cache coherency across multiple mountpoints
81  * of the same network filesystem on a single client.
82  */
83 struct test_file {
84         char *path;
85         int fd;
86         int o_direct;
87 } *test_files = NULL, *tf;
88
89 int num_test_files;
90
91 enum fd_iteration_policy {
92         FD_SINGLE,
93         FD_ROTATE,
94         FD_RANDOM,
95 };
96
97 int fd_policy = FD_RANDOM;
98 int fd_last;
99
100 /*
101  *      A log entry is an operation and a bunch of arguments.
102  */
103
104 struct log_entry {
105         int operation;
106         int args[3];
107         struct timeval tv;
108         const struct test_file *tf;
109 };
110
111 #define LOGSIZE 100000
112
113 struct log_entry oplog[LOGSIZE]; /* the log */
114 int logptr; /* current position in log */
115 int logcount; /* total ops */
116 int jmpbuf_good;
117 jmp_buf jmpbuf;
118
119 /*
120  * Define operations
121  */
122
123 /* common operations */
124 #define OP_READ         0
125 #define OP_WRITE        1
126 #define OP_MAPREAD      2
127 #define OP_MAPWRITE     3
128 #define OP_MAX_LITE     4
129
130 /* !lite operations */
131 #define OP_TRUNCATE             4
132 #define OP_FALLOCATE            5
133 #define OP_PUNCH_HOLE           6
134 #define OP_ZERO_RANGE           7
135 #define OP_CLOSEOPEN            8
136 #define OP_MAX_FULL             9
137
138 #define OP_SKIPPED 101
139 /* _GNU_SOURCE defines O_DIRECT as 14th bit which is 0x4000(16384) */
140 #define OP_DIRECT  16384
141
142 #ifndef FALLOC_FL_PUNCH_HOLE
143 #define FALLOC_FL_PUNCH_HOLE 0x02 /* de-allocates range */
144 #endif
145
146 #ifndef FALLOC_FL_KEEP_SIZE
147 #define FALLOC_FL_KEEP_SIZE 0x01 /* default is extend size */
148 #endif
149
150 #ifndef FALLOC_FL_ZERO_RANGE
151 #define FALLOC_FL_ZERO_RANGE 0x10 /* convert range to zeros */
152 #endif
153
154
155 char *original_buf; /* a pointer to the original data */
156 char *good_buf; /* a pointer to the correct data */
157 char *temp_buf; /* a pointer to the current data */
158 char *fname; /* name of our test file */
159 char logfile[PATH_MAX]; /* name of our log file */
160 char goodfile[PATH_MAX]; /* name of our test file */
161
162 struct timeval tv; /* time current operation started */
163 off_t file_size;
164 off_t biggest;
165 char state[256];
166 unsigned long testcalls; /* calls to function "test" */
167
168 long simulatedopcount;                  /* -b flag */
169 int closeprob;                          /* -c flag */
170 int debug ;                             /* -d flag */
171 long debugstart;                        /* -D flag */
172 int flush;                              /* -f flag */
173 int do_fsync;                           /* -y flag */
174 long maxfilelen = 256 * 1024;           /* -l flag */
175 int sizechecks = 1;                     /* -n flag disables them */
176 int maxoplen = 64 * 1024;               /* -o flag */
177 int quiet;                              /* -q flag */
178 long progressinterval;                  /* -p flag */
179 int readbdy = 1;                        /* -r flag */
180 int style;                              /* -s flag */
181 int truncbdy = 1;                       /* -t flag */
182 int writebdy = 1;                       /* -w flag */
183 long monitorstart = -1;                 /* -m flag */
184 long monitorend = -1;                   /* -m flag */
185 int lite;                               /* -L flag */
186 long numops = -1;                       /* -N flag */
187 int randomoplen = 1;                    /* -O flag disables it */
188 int seed = 1;                           /* -S flag */
189 int mapped_writes = 1;                  /* -W flag disables */
190 int fallocate_calls = 1;                /* -F flag disables */
191 int punch_hole_calls = 1;               /* -H flag disables */
192 int zero_range_calls = 1;               /* -z flag disables */
193 int mapped_reads = 1;                   /* -R flag disables it */
194 int fsxgoodfd;
195 int o_direct;                           /* -Z */
196 int fl_keep_size;
197
198 int page_size;
199 int page_mask;
200
201 FILE *fsxlogf;
202 int badoff = -1;
203
204 void
205 vwarnc(code, fmt, ap)
206         int code;
207         const char *fmt;
208         va_list ap;
209 {
210         fprintf(stderr, "fsx: ");
211         if (fmt) {
212                 vfprintf(stderr, fmt, ap);
213                 fprintf(stderr, ": ");
214         }
215         fprintf(stderr, "%s\n", strerror(code));
216 }
217
218 void
219 __attribute__((format(__printf__, 1, 2)))
220 warn(const char *fmt, ...)
221 {
222         va_list ap;
223
224         va_start(ap, fmt);
225         vwarnc(errno, fmt, ap);
226         va_end(ap);
227 }
228
229 void
230 __attribute__((format(__printf__, 1, 2)))
231 prt(char *fmt, ...)
232 {
233         va_list args;
234
235         va_start(args, fmt);
236         vfprintf(stdout, fmt, args);
237         va_end(args);
238
239         if (fsxlogf) {
240                 va_start(args, fmt);
241                 vfprintf(fsxlogf, fmt, args);
242                 va_end(args);
243         }
244 }
245
246 /*
247  * prterr() is now a macro. It internally calls ptrerr_func()
248  * which transparently handles passing of function name.
249  * This version also keeps checkpatch happy.
250  */
251 void
252 ptrerr_func(const char *func, const char *prefix)
253 {
254         prt("%s: %s%s%s\n", func, prefix, prefix ? ": " : "", strerror(errno));
255 }
256 #define prterr(prefix) ptrerr_func(__func__, prefix)
257
258 void
259 log4(int operation, int arg0, int arg1, int arg2)
260 {
261         struct log_entry *le;
262
263         le = &oplog[logptr];
264         le->operation = operation;
265         le->args[0] = arg0;
266         le->args[1] = arg1;
267         le->args[2] = arg2;
268         gettimeofday(&tv, NULL);
269         le->tv = tv;
270         le->tf = tf;
271         logptr++;
272         logcount++;
273         if (logptr >= LOGSIZE)
274                 logptr = 0;
275 }
276
277 const char *
278 fill_tf_buf(const struct test_file *tf)
279 {
280         static int max_tf_len;
281         static char tf_buf[32];
282
283         if (fd_policy == FD_SINGLE)
284                 return "";
285
286         if (max_tf_len == 0)
287                 max_tf_len = scnprintf(tf_buf, sizeof(tf_buf) - 1,
288                                       "%u", num_test_files - 1);
289
290         snprintf(tf_buf, sizeof(tf_buf), "[%0*lu]", max_tf_len,
291                 (unsigned long)(tf - test_files));
292
293         return tf_buf;
294 }
295
296 void
297 logdump(void)
298 {
299         int i, count, down;
300         struct log_entry *lp;
301         char *falloc_type[3] = {"PAST_EOF", "EXTENDING", "INTERIOR"};
302
303         prt("LOG DUMP (%d total operations):\n", logcount);
304         if (logcount < LOGSIZE) {
305                 i = 0;
306                 count = logcount;
307         } else {
308                 i = logptr;
309                 count = LOGSIZE;
310         }
311         for ( ; count > 0; count--) {
312                 int opnum;
313
314                 opnum = i + 1 + (logcount / LOGSIZE) * LOGSIZE;
315                 lp = &oplog[i];
316                 prt("%d%s: %lu.%06u ", opnum, fill_tf_buf(lp->tf),
317                     lp->tv.tv_sec, (int)lp->tv.tv_usec);
318
319                 switch (lp->operation) {
320                 case OP_MAPREAD:
321                         prt("MAPREAD  0x%05x thru 0x%05x (0x%05x bytes)",
322                             lp->args[0], lp->args[0] + lp->args[1] - 1,
323                             lp->args[1]);
324                         if (badoff >= lp->args[0] && badoff <
325                                                      lp->args[0] + lp->args[1])
326                                 prt("\t***RRRR***");
327                         break;
328                 case OP_MAPWRITE:
329                         prt("MAPWRITE 0x%05x thru 0x%05x (0x%05x bytes)",
330                             lp->args[0], lp->args[0] + lp->args[1] - 1,
331                             lp->args[1]);
332                         if (badoff >= lp->args[0] && badoff <
333                                                      lp->args[0] + lp->args[1])
334                                 prt("\t******WWWW");
335                         break;
336                 case OP_READ:
337                 case OP_READ + OP_DIRECT:
338                         prt("READ%s  0x%05x thru 0x%05x (0x%05x bytes)",
339                             lp->operation & OP_DIRECT ? "_OD" : "   ",
340                             lp->args[0], lp->args[0] + lp->args[1] - 1,
341                             lp->args[1]);
342                         if (badoff >= lp->args[0] &&
343                             badoff < lp->args[0] + lp->args[1])
344                                 prt("\t***RRRR***");
345                         break;
346                 case OP_WRITE:
347                 case OP_WRITE + OP_DIRECT:
348                         prt("WRITE%s 0x%05x thru 0x%05x (0x%05x bytes)",
349                             lp->operation & OP_DIRECT ? "_OD" : "   ",
350                             lp->args[0], lp->args[0] + lp->args[1] - 1,
351                             lp->args[1]);
352                         if (lp->args[0] > lp->args[2])
353                                 prt(" HOLE");
354                         else if (lp->args[0] + lp->args[1] > lp->args[2])
355                                 prt(" EXTEND");
356                         if ((badoff >= lp->args[0] || badoff >= lp->args[2]) &&
357                             badoff < lp->args[0] + lp->args[1])
358                                 prt("\t***WWWW");
359                         break;
360                 case OP_TRUNCATE:
361                         down = lp->args[0] < lp->args[1];
362                         prt("TRUNCATE %s\tfrom 0x%05x to 0x%05x",
363                             down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
364                         if (badoff >= lp->args[!down] &&
365                             badoff < lp->args[!!down])
366                                 prt("\t******WWWW");
367                         break;
368                 case OP_FALLOCATE:
369                         /* 0: offset 1: length 2: where alloced */
370                         prt("FALLOC  \tfrom 0x%05x to 0x%05x\t(0x%05x bytes)%s",
371                             lp->args[0], lp->args[0] + lp->args[1],
372                             lp->args[1], falloc_type[lp->args[2]]);
373                         if (badoff >= lp->args[0] &&
374                             badoff < lp->args[0] + lp->args[1])
375                                 prt("\t******FFFF");
376                         break;
377                 case OP_PUNCH_HOLE:
378                         prt("PUNCH    0x%05x thru 0x%05x\t(0x%05x bytes)",
379                             lp->args[0], lp->args[0] + lp->args[1] - 1,
380                             lp->args[1]);
381                         if (badoff >= lp->args[0] && badoff <
382                                                      lp->args[0] + lp->args[1])
383                                 prt("\t******PPPP");
384                         break;
385                 case OP_ZERO_RANGE:
386                         prt("ZERO     0x%05x thru 0x%05x\t(0x%05x bytes)",
387                             lp->args[0], lp->args[0] + lp->args[1] - 1,
388                             lp->args[1]);
389                         if (badoff >= lp->args[0] && badoff <
390                                                      lp->args[0] + lp->args[1])
391                                 prt("\t******ZZZZ");
392                         break;
393                 case OP_CLOSEOPEN:
394                 case OP_CLOSEOPEN + OP_DIRECT:
395                         prt("CLOSE/OPEN%s",
396                             lp->operation & OP_DIRECT ? "_OD" : "   ");
397                         break;
398                 case OP_SKIPPED:
399                         prt("SKIPPED (no operation)");
400                         break;
401                 default:
402                         prt("BOGUS LOG ENTRY (operation code = %d)!",
403                             lp->operation);
404                 }
405                 prt("\n");
406                 i++;
407                 if (i == LOGSIZE)
408                         i = 0;
409         }
410 }
411
412 void
413 save_buffer(char *buffer, off_t bufferlength, int fd)
414 {
415         off_t ret;
416         ssize_t byteswritten;
417
418         if (fd <= 0 || bufferlength == 0)
419                 return;
420
421         if (bufferlength > INT_MAX) {
422                 prt("fsx flaw: overflow in %s\n", __func__);
423                 exit(67);
424         }
425         if (lite) {
426                 off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END);
427
428                 if (size_by_seek == (off_t)-1) {
429                         prterr("lseek eof");
430                 } else if (bufferlength > size_by_seek) {
431                         warn("%s: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n",
432                              __func__, (unsigned long long)size_by_seek,
433                              (unsigned long long)bufferlength);
434                         bufferlength = size_by_seek;
435                 }
436         }
437
438         ret = lseek(fd, (off_t)0, SEEK_SET);
439         if (ret == (off_t)-1)
440                 prterr("lseek 0");
441
442         byteswritten = write(fd, buffer, (size_t)bufferlength);
443         if (byteswritten != bufferlength) {
444                 if (byteswritten == -1)
445                         prterr("write");
446                 else
447                         warn("%s: short write, 0x%x bytes instead of 0x%llx\n",
448                              __func__, (unsigned int)byteswritten,
449                              (unsigned long long)bufferlength);
450         }
451 }
452
453 void
454 report_failure(int status)
455 {
456         logdump();
457
458         if (fsxgoodfd) {
459                 if (good_buf) {
460                         save_buffer(good_buf, file_size, fsxgoodfd);
461                         prt("Correct content saved for comparison\n");
462                         prt("(maybe hexdump \"%s\" vs \"%s\")\n",
463                             fname, goodfile);
464                 }
465                 close(fsxgoodfd);
466         }
467         exit(status);
468 }
469
470 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
471                       *(((unsigned char *)(cp)) + 1)))
472
473 void
474 check_buffers(unsigned int offset, unsigned int size)
475 {
476         unsigned char c, t;
477         unsigned int i = 0;
478         unsigned int n = 0;
479         unsigned int op = 0;
480         unsigned int bad = 0;
481
482         if (memcmp(good_buf + offset, temp_buf, size) != 0) {
483                 prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n",
484                     offset, size);
485                 prt("OFFSET\tGOOD\tBAD\tRANGE\n");
486                 while (size > 0) {
487                         c = good_buf[offset];
488                         t = temp_buf[i];
489                         if (c != t) {
490                                 if (n == 0) {
491                                         bad = short_at(&temp_buf[i]);
492                                         prt("%#07x\t%#06x\t%#06x", offset,
493                                             short_at(&good_buf[offset]), bad);
494                                         op = temp_buf[offset & 1 ? i + 1 : i];
495                                 }
496                                 n++;
497                                 badoff = offset;
498                         }
499                         offset++;
500                         i++;
501                         size--;
502                 }
503                 if (n) {
504                         prt("\t%#7x\n", n);
505                         if (bad)
506                                 prt("operation# (mod 256) for the bad data may be %u\n",
507                                     ((unsigned int)op & 0xff));
508                         else
509                                 prt("operation# (mod 256) for the bad data unknown, check HOLE and EXTEND ops\n");
510                 } else {
511                         prt("????????????????\n");
512                 }
513                 report_failure(110);
514         }
515 }
516
517 struct test_file *
518 get_tf(void)
519 {
520         unsigned int index = 0;
521
522         switch (fd_policy) {
523         case FD_ROTATE:
524                 index = fd_last++;
525                 break;
526         case FD_RANDOM:
527                 index = random();
528                 break;
529         case FD_SINGLE:
530                 index = 0;
531                 break;
532         default:
533                 prt("unknown policy");
534                 exit(1);
535                 break;
536         }
537         return &test_files[index % num_test_files];
538 }
539
540 void
541 assign_fd_policy(char *policy)
542 {
543         if (!strcmp(policy, "random")) {
544                 fd_policy = FD_RANDOM;
545         } else if (!strcmp(policy, "rotate")) {
546                 fd_policy = FD_ROTATE;
547         } else {
548                 prt("unknown -I policy: '%s'\n", policy);
549                 exit(1);
550         }
551 }
552
553 int
554 get_fd(void)
555 {
556         struct test_file *tf = get_tf();
557
558         return tf->fd;
559 }
560
561 static const char *my_basename(const char *path)
562 {
563         char *c = strrchr(path, '/');
564
565         return c ? c++ : path;
566 }
567
568 void
569 open_test_files(char **argv, int argc)
570 {
571         struct test_file *tf;
572         int i;
573
574         num_test_files = argc;
575         if (num_test_files == 1)
576                 fd_policy = FD_SINGLE;
577
578         test_files = calloc(num_test_files, sizeof(*test_files));
579         if (!test_files) {
580                 prterr("reallocating space for test files");
581                 exit(1);
582         }
583
584         for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
585                 tf->path = argv[i];
586 #ifdef O_DIRECT
587                 tf->o_direct = (random() % (o_direct + 1)) ? OP_DIRECT : 0;
588 #endif
589                 tf->fd = open(tf->path,
590                               O_RDWR | (lite ? 0 : O_CREAT | O_TRUNC) |
591                               tf->o_direct, 0666);
592                 if (tf->fd < 0) {
593                         prterr(tf->path);
594                         exit(91);
595                 }
596         }
597
598         if (quiet || fd_policy == FD_SINGLE)
599                 return;
600
601         for (i = 0, tf = test_files; i < num_test_files; i++, tf++)
602                 prt("fd %d: %s\n", i, tf->path);
603 }
604
605 void
606 close_test_files(void)
607 {
608         int i;
609         struct test_file *tf;
610
611         for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
612                 if (close(tf->fd)) {
613                         prterr("close");
614                         report_failure(99);
615                 }
616         }
617 }
618
619 void
620 check_size(void)
621 {
622         struct stat statbuf;
623         off_t size_by_seek;
624         int fd = get_fd();
625
626         if (fstat(fd, &statbuf)) {
627                 prterr("fstat");
628                 statbuf.st_size = -1;
629         }
630         size_by_seek = lseek(fd, (off_t)0, SEEK_END);
631         if (file_size != statbuf.st_size || file_size != size_by_seek) {
632                 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
633                     (unsigned long long)file_size,
634                     (unsigned long long)statbuf.st_size,
635                     (unsigned long long)size_by_seek);
636                 report_failure(120);
637         }
638 }
639
640 void
641 check_trunc_hack(void)
642 {
643         struct stat statbuf;
644         int fd = get_fd();
645
646         /* should not ignore ftruncate(2)'s return value */
647         if (ftruncate(fd, (off_t)0) < 0) {
648                 prterr("trunc_hack: ftruncate(0)");
649                 exit(1);
650         }
651         if (ftruncate(fd, (off_t)100000) < 0) {
652                 prterr("trunc_hack: ftruncate(100000)");
653                 exit(1);
654         }
655         if (fstat(fd, &statbuf)) {
656                 prterr("trunc_hack: fstat");
657                 statbuf.st_size = -1;
658         }
659         if (statbuf.st_size != (off_t)100000) {
660                 prt("no extend on truncate! not posix!\n");
661                 exit(130);
662         }
663         if (ftruncate(fd, 0) < 0) {
664                 prterr("trunc_hack: ftruncate(0) (2nd call)");
665                 exit(1);
666         }
667 }
668
669 void
670 output_line(struct test_file *tf, int op, unsigned int offset,
671             unsigned int size)
672 {
673         char *ops[] = {
674                 [OP_READ] = "read",
675                 [OP_WRITE] = "write",
676                 [OP_TRUNCATE] = "trunc from",
677                 [OP_MAPREAD] = "mapread",
678                 [OP_MAPWRITE] = "mapwrite",
679                 [OP_READ + OP_DIRECT] = "read_OD",
680                 [OP_WRITE + OP_DIRECT] = "write_OD",
681                 [OP_FALLOCATE] = "fallocate",
682         };
683
684         /* W. */
685         if (!(!quiet &&
686             ((progressinterval && testcalls % progressinterval == 0) ||
687             (debug && (monitorstart == -1 ||
688             (offset + size > monitorstart &&
689             (monitorend == -1 || offset <= monitorend)))))))
690                 return;
691
692         prt("%06lu%s %lu.%06u %-10s %#08x %s %#08x\t(0x05%x bytes)\n",
693             testcalls, fill_tf_buf(tf), tv.tv_sec, (int)tv.tv_usec,
694             ops[op], offset, op == OP_TRUNCATE ? " to " : "thru",
695             offset + size - 1, (int)size < 0 ? -(int)size : size);
696 }
697
698 void output_debug(unsigned int offset, unsigned int size, const char *what)
699 {
700         struct timeval t;
701
702         if (!quiet && (debug > 1 && (monitorstart == -1 ||
703             (offset + size >= monitorstart &&
704              (monitorend == -1 || offset <= monitorend))))) {
705                 gettimeofday(&t, NULL);
706                 prt("       %lu.%06u %s\n", t.tv_sec, (int)t.tv_usec, what);
707         }
708 }
709
710 void
711 doflush(unsigned int offset, unsigned int size)
712 {
713         unsigned int pg_offset;
714         unsigned int map_size;
715         char *p;
716         struct test_file *tf = get_tf();
717         int fd = tf->fd;
718
719         if (tf->o_direct)
720                 return;
721
722         pg_offset = offset & page_mask;
723         map_size  = pg_offset + size;
724
725         p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
726                          MAP_FILE | MAP_SHARED, fd,
727                          (off_t)(offset - pg_offset));
728         if (p == (char *)-1) {
729                 prterr("mmap");
730                 report_failure(202);
731         }
732         if (msync(p, map_size, MS_INVALIDATE) != 0) {
733                 prterr("msync");
734                 report_failure(203);
735         }
736         if (munmap(p, map_size) != 0) {
737                 prterr("munmap");
738                 report_failure(204);
739         }
740         output_debug(offset, size, "flush done");
741 }
742
743 void
744 doread(unsigned int offset, unsigned int size)
745 {
746         off_t ret;
747         unsigned int iret;
748         struct test_file *tf = get_tf();
749         int fd = tf->fd;
750
751         offset -= offset % readbdy;
752         if (tf->o_direct)
753                 size -= size % readbdy;
754
755         if (size == 0) {
756                 if (!quiet && testcalls > simulatedopcount && !tf->o_direct)
757                         prt("skipping zero size read\n");
758                 log4(OP_SKIPPED, OP_READ, offset, size);
759                 return;
760         }
761         if (size + offset > file_size) {
762                 if (!quiet && testcalls > simulatedopcount)
763                         prt("skipping seek/read past end of file\n");
764                 log4(OP_SKIPPED, OP_READ, offset, size);
765                 return;
766         }
767
768         log4(OP_READ + tf->o_direct, offset, size, 0);
769
770         if (testcalls <= simulatedopcount)
771                 return;
772
773         output_line(tf, OP_READ + tf->o_direct, offset, size);
774
775         ret = lseek(fd, (off_t)offset, SEEK_SET);
776         if (ret == (off_t)-1) {
777                 prterr("lseek");
778                 report_failure(140);
779         }
780         iret = read(fd, temp_buf, size);
781         output_debug(offset, size, "read done");
782         if (iret != size) {
783                 if (iret == -1)
784                         prterr("read");
785                 else
786                         prt("short read: 0x%x bytes instead of 0x%x\n",
787                             iret, size);
788                 report_failure(141);
789         }
790         check_buffers(offset, size);
791 }
792
793 void
794 check_eofpage(char *s, unsigned int offset, char *p, int size)
795 {
796         long last_page, should_be_zero;
797
798         if (offset + size <= (file_size & ~page_mask))
799                 return;
800         /*
801          * we landed in the last page of the file
802          * test to make sure the VM system provided 0's
803          * beyond the true end of the file mapping
804          * (as required by mmap def in 1996 posix 1003.1)
805          */
806         last_page = ((long)p + (offset & page_mask) + size) & ~page_mask;
807
808         for (should_be_zero = last_page + (file_size & page_mask);
809              should_be_zero < last_page + page_size;
810              should_be_zero++)
811                 if (*(char *)should_be_zero) {
812                         prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%lx is 0x%04x\n",
813                             s, (long long)file_size - 1,
814                             should_be_zero & page_mask,
815                             short_at(should_be_zero));
816                         report_failure(205);
817                 }
818 }
819
820 void
821 domapread(unsigned int offset, unsigned int size)
822 {
823         unsigned int pg_offset;
824         unsigned int map_size;
825         char *p;
826         int fd;
827
828         offset -= offset % readbdy;
829         tf = get_tf();
830         fd = tf->fd;
831         if (size == 0) {
832                 if (!quiet && testcalls > simulatedopcount)
833                         prt("skipping zero size read\n");
834                 log4(OP_SKIPPED, OP_MAPREAD, offset, size);
835                 return;
836         }
837         if (size + offset > file_size) {
838                 if (!quiet && testcalls > simulatedopcount)
839                         prt("skipping seek/read past end of file\n");
840                 log4(OP_SKIPPED, OP_MAPREAD, offset, size);
841                 return;
842         }
843
844         log4(OP_MAPREAD, offset, size, 0);
845
846         if (testcalls <= simulatedopcount)
847                 return;
848
849         output_line(tf, OP_MAPREAD, offset, size);
850
851         pg_offset = offset & page_mask;
852         map_size  = pg_offset + size;
853
854         p = mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd,
855                  (off_t)(offset - pg_offset));
856         if (p == MAP_FAILED) {
857                 prterr("mmap");
858                 report_failure(190);
859         }
860         output_debug(offset, size, "mmap done");
861         if (setjmp(jmpbuf) == 0) {
862                 jmpbuf_good = 1;
863                 memcpy(temp_buf, p + pg_offset, size);
864                 check_eofpage("Read", offset, p, size);
865                 jmpbuf_good = 0;
866         } else {
867                 report_failure(1901);
868         }
869         output_debug(offset, size, "memcpy done");
870         if (munmap(p, map_size) != 0) {
871                 prterr("munmap");
872                 report_failure(191);
873         }
874         output_debug(offset, size, "munmap done");
875
876         check_buffers(offset, size);
877 }
878
879 void
880 gendata(char *original_buf, char *good_buf, unsigned int offset,
881         unsigned int size)
882 {
883         while (size--) {
884                 good_buf[offset] = testcalls % 256;
885                 if (offset % 2)
886                         good_buf[offset] += original_buf[offset];
887                 offset++;
888         }
889 }
890
891 void
892 dowrite(unsigned int offset, unsigned int size)
893 {
894         off_t ret;
895         unsigned int iret;
896         int fd;
897
898         tf = get_tf();
899         fd = tf->fd;
900         offset -= offset % writebdy;
901         if (tf->o_direct)
902                 size -= size % writebdy;
903         if (size == 0) {
904                 if (!quiet && testcalls > simulatedopcount && !tf->o_direct)
905                         prt("skipping zero size write\n");
906                 log4(OP_SKIPPED, OP_WRITE, offset, size);
907                 return;
908         }
909
910         log4(OP_WRITE + tf->o_direct, offset, size, file_size);
911
912         gendata(original_buf, good_buf, offset, size);
913         if (file_size < offset + size) {
914                 if (file_size < offset)
915                         memset(good_buf + file_size, '\0', offset - file_size);
916                 file_size = offset + size;
917                 if (lite) {
918                         warn("Lite file size bug in fsx!");
919                         report_failure(149);
920                 }
921         }
922
923         if (testcalls <= simulatedopcount)
924                 return;
925
926         output_line(tf, OP_WRITE + tf->o_direct, offset, size);
927
928         ret = lseek(fd, (off_t)offset, SEEK_SET);
929         if (ret == (off_t)-1) {
930                 prterr("lseek");
931                 report_failure(150);
932         }
933         iret = write(fd, good_buf + offset, size);
934         output_debug(offset, size, "write done");
935         if (iret != size) {
936                 if (iret == -1)
937                         prterr("write");
938                 else
939                         prt("short write: 0x%x bytes instead of 0x%x\n",
940                             iret, size);
941                 report_failure(151);
942         }
943         if (do_fsync) {
944                 if (fsync(fd)) {
945                         prt("fsync() failed: %s\n", strerror(errno));
946                         report_failure(152);
947                 }
948                 output_debug(offset, size, "fsync done");
949         }
950         if (flush) {
951                 doflush(offset, size);
952                 output_debug(offset, size, "flush done");
953         }
954 }
955
956 void
957 domapwrite(unsigned int offset, unsigned int size)
958 {
959         unsigned int pg_offset;
960         unsigned int map_size;
961         off_t cur_filesize;
962         char *p;
963         int fd;
964
965         tf = get_tf();
966         fd = tf->fd;
967         offset -= offset % writebdy;
968         if (size == 0) {
969                 if (!quiet && testcalls > simulatedopcount)
970                         prt("skipping zero size write\n");
971                 log4(OP_SKIPPED, OP_MAPWRITE, offset, size);
972                 return;
973         }
974         cur_filesize = file_size;
975
976         log4(OP_MAPWRITE, offset, size, 0);
977
978         gendata(original_buf, good_buf, offset, size);
979         if (file_size < offset + size) {
980                 if (file_size < offset)
981                         memset(good_buf + file_size, '\0', offset - file_size);
982                 file_size = offset + size;
983                 if (lite) {
984                         warn("Lite file size bug in fsx!");
985                         report_failure(200);
986                 }
987         }
988
989         if (testcalls <= simulatedopcount)
990                 return;
991
992         output_line(tf, OP_MAPWRITE, offset, size);
993
994         if (file_size > cur_filesize) {
995                 if (ftruncate(fd, file_size) == -1) {
996                         prterr("ftruncate");
997                         exit(201);
998                 }
999                 output_debug(offset, size, "truncate done");
1000         }
1001         pg_offset = offset & page_mask;
1002         map_size  = pg_offset + size;
1003
1004         p = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED,
1005                  fd, (off_t)(offset - pg_offset));
1006         if (p == MAP_FAILED) {
1007                 prterr("mmap");
1008                 report_failure(202);
1009         }
1010         output_debug(offset, map_size, "mmap done");
1011         if (setjmp(jmpbuf) == 0) {
1012                 jmpbuf_good = 1;
1013                 memcpy(p + pg_offset, good_buf + offset, size);
1014                 if (msync(p, map_size, MS_SYNC) != 0) {
1015                         prterr("msync");
1016                         report_failure(203);
1017                 }
1018                 check_eofpage("Write", offset, p, size);
1019                 jmpbuf_good = 0;
1020         } else {
1021                 report_failure(2021);
1022         }
1023         output_debug(offset, map_size, "msync done");
1024         if (munmap(p, map_size) != 0) {
1025                 prterr("munmap");
1026                 report_failure(204);
1027         }
1028         output_debug(offset, map_size, "munmap done");
1029 }
1030
1031 void
1032 dotruncate(unsigned int size)
1033 {
1034         int oldsize = file_size;
1035         int fd;
1036
1037         tf = get_tf();
1038         fd = tf->fd;
1039         size -= size % truncbdy;
1040         if (size > biggest) {
1041                 biggest = size;
1042                 if (!quiet && testcalls > simulatedopcount)
1043                         prt("truncating to largest ever: 0x%x\n", size);
1044         }
1045
1046         log4(OP_TRUNCATE, size, (unsigned int)file_size, 0);
1047
1048         if (size > file_size)
1049                 memset(good_buf + file_size, '\0', size - file_size);
1050         file_size = size;
1051
1052         if (testcalls <= simulatedopcount)
1053                 return;
1054
1055         output_line(tf, OP_TRUNCATE, oldsize, size - oldsize);
1056
1057         if (ftruncate(fd, (off_t)size) == -1) {
1058                 prt("ftruncate: 0x%x\n", size);
1059                 prterr("ftruncate");
1060                 report_failure(160);
1061         }
1062         output_debug(size, 0, "truncate done");
1063 }
1064
1065 void
1066 do_punch_hole(unsigned int offset, unsigned int length)
1067 {
1068         int max_offset = 0;
1069         int max_len = 0;
1070         int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
1071         int fd;
1072
1073         tf = get_tf();
1074         fd = tf->fd;
1075         if (length == 0) {
1076                 if (!quiet && testcalls > simulatedopcount) {
1077                         prt("skipping zero length punch hole\n");
1078                         log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length);
1079                 }
1080                 return;
1081         }
1082
1083         if (file_size <= (loff_t)offset) {
1084                 if (!quiet && testcalls > simulatedopcount) {
1085                         prt("skipping hole punch off the end of the file\n");
1086                         log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length);
1087                 }
1088                 return;
1089         }
1090
1091         log4(OP_PUNCH_HOLE, offset, length, 0);
1092
1093         if (testcalls <= simulatedopcount)
1094                 return;
1095
1096         output_line(tf, OP_PUNCH_HOLE, offset, length);
1097         if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1098                 prt("punch hole: %x to %x\n", offset, length);
1099                 prterr("fallocate");
1100                 report_failure(161);
1101         }
1102         output_debug(offset, length, "zero_range done");
1103
1104         max_offset = offset < file_size ? offset : file_size;
1105         max_len = max_offset + length <= file_size ? length :
1106                         file_size - max_offset;
1107         memset(good_buf + max_offset, '\0', max_len);
1108 }
1109
1110 void
1111 do_zero_range(unsigned int offset, unsigned int length)
1112 {
1113         unsigned int end_offset;
1114         int mode = FALLOC_FL_ZERO_RANGE;
1115         int keep_size;
1116         int fd;
1117
1118         tf = get_tf();
1119         fd = tf->fd;
1120         if (length == 0) {
1121                 if (!quiet && testcalls > simulatedopcount) {
1122                         prt("skipping zero length zero range\n");
1123                         log4(OP_SKIPPED, OP_ZERO_RANGE, offset, length);
1124                 }
1125                 return;
1126         }
1127
1128         keep_size = random() % 2;
1129
1130         end_offset = keep_size ? 0 : offset + length;
1131
1132         if (end_offset > biggest) {
1133                 biggest = end_offset;
1134                 if (!quiet && testcalls > simulatedopcount)
1135                         prt("zero_range to largest ever: 0x%x\n", end_offset);
1136         }
1137
1138         /*
1139          * last arg matches fallocate string array index in logdump:
1140          * 0: allocate past EOF
1141          * 1: extending prealloc
1142          * 2: interior prealloc
1143          */
1144         log4(OP_ZERO_RANGE, offset, length,
1145              (end_offset > file_size) ? (keep_size ? 0 : 1) : 2);
1146
1147         if (testcalls <= simulatedopcount)
1148                 return;
1149
1150         output_line(tf, OP_TRUNCATE, offset, length);
1151
1152         if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1153                 prt("pzero range: %x to %x\n", offset, length);
1154                 prterr("fallocate");
1155                 report_failure(161);
1156         }
1157         output_debug(offset, length, "zero_range done");
1158
1159         memset(good_buf + offset, '\0', length);
1160 }
1161
1162 /*
1163  * fallocate is basically a no-op unless extending,
1164  * then a lot like a truncate
1165  */
1166 void
1167 do_preallocate(unsigned int offset, unsigned int length)
1168 {
1169         off_t end_offset;
1170         off_t new_offset;
1171         int keep_size;
1172         int fd;
1173         struct stat statbufs;
1174
1175         tf = get_tf();
1176         fd = tf->fd;
1177         if (length == 0) {
1178                 if (!quiet && testcalls > simulatedopcount)
1179                         prt("skipping zero length fallocate\n");
1180                 log4(OP_SKIPPED, OP_FALLOCATE, offset, length);
1181                 return;
1182         }
1183
1184         keep_size = fl_keep_size && (random() % 2);
1185
1186         end_offset = keep_size ? 0 : offset + length;
1187
1188         if (end_offset > biggest) {
1189                 biggest = end_offset;
1190                 if (!quiet && testcalls > simulatedopcount)
1191                         prt("fallocating to largest ever: 0x%jx\n", end_offset);
1192         }
1193
1194         /*
1195          * last arg matches fallocate string array index in logdump:
1196          * 0: allocate past EOF
1197          * 1: extending prealloc
1198          * 2: interior prealloc
1199          */
1200         log4(OP_FALLOCATE, offset, length, (end_offset > file_size) ?
1201              (keep_size ? 0 : 1) : 2);
1202
1203         if (end_offset > file_size) {
1204                 memset(good_buf + file_size, '\0', end_offset - file_size);
1205                 file_size = end_offset;
1206         } else {
1207                 new_offset = file_size - (offset + length);
1208                 length = length + new_offset;
1209         }
1210
1211         if (testcalls <= simulatedopcount)
1212                 return;
1213
1214         fstat(fd, &statbufs);
1215         if (fallocate(fd, keep_size ? FALLOC_FL_KEEP_SIZE : 0, (loff_t)offset,
1216                       (loff_t)length) == -1) {
1217                 prt("fallocate: %x to %x\n", offset, length);
1218                 prterr("fallocate");
1219                 report_failure(161);
1220         }
1221         output_line(tf, OP_FALLOCATE, offset, length);
1222         output_debug(offset, length, "fallocate done");
1223 }
1224
1225 void
1226 writefileimage()
1227 {
1228         ssize_t iret;
1229         int fd = get_fd();
1230
1231         if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
1232                 prterr("lseek");
1233                 report_failure(171);
1234         }
1235         iret = write(fd, good_buf, file_size);
1236         if ((off_t)iret != file_size) {
1237                 if (iret == -1)
1238                         prterr("write");
1239                 else
1240                         prt("short write: 0x%lx bytes instead of 0x%llx\n",
1241                             (unsigned long)iret, (unsigned long long)file_size);
1242                 report_failure(172);
1243         }
1244         if (lite ? 0 : ftruncate(fd, file_size) == -1) {
1245                 prt("ftruncate2: %llx\n", (unsigned long long)file_size);
1246                 prterr("ftruncate");
1247                 report_failure(173);
1248         }
1249 }
1250
1251 void
1252 docloseopen(void)
1253 {
1254         int direct = 0;
1255         const char *tf_num = "";
1256
1257         if (testcalls <= simulatedopcount)
1258                 return;
1259
1260         tf = get_tf();
1261 #ifdef O_DIRECT
1262         direct = (random() % (o_direct + 1)) ? OP_DIRECT : 0;
1263 #endif
1264         log4(OP_CLOSEOPEN + direct, file_size, (unsigned int)file_size, 0);
1265
1266         if (fd_policy != FD_SINGLE)
1267                 tf_num = fill_tf_buf(tf);
1268
1269         if (debug)
1270                 prt("%06lu %lu.%06u %sclose/open%s\n", testcalls, tv.tv_sec,
1271                     (int)tv.tv_usec, tf_num, direct ? "(O_DIRECT)" : "");
1272         if (close(tf->fd))
1273                 report_failure(180);
1274
1275         output_debug(monitorstart, 0, "close done");
1276         tf->o_direct = direct;
1277         tf->fd = open(tf->path, O_RDWR | tf->o_direct, 0);
1278         if (tf->fd < 0) {
1279                 prterr(tf->o_direct ? "open(O_DIRECT)" : "open");
1280                 report_failure(181);
1281         }
1282         output_debug(monitorstart, 0,
1283                      tf->o_direct ? "open(O_DIRECT) done" : "open done");
1284 }
1285
1286 #define TRIM_OFF_LEN(off, len, size)    \
1287 do {                                    \
1288         if (size)                       \
1289                 (off) %= (size);        \
1290         else                            \
1291                 (off) = 0;              \
1292         if ((off) + (len) > (size))     \
1293                 (len) = (size) - (off); \
1294 } while (0)
1295
1296 void
1297 test(void)
1298 {
1299         unsigned long offset;
1300         unsigned long size = maxoplen;
1301         unsigned long rv = random();
1302         unsigned long op;
1303         int closeopen = 0;
1304
1305         if (simulatedopcount > 0 && testcalls == simulatedopcount)
1306                 writefileimage();
1307
1308         testcalls++;
1309
1310         if (closeprob)
1311                 closeopen = (rv >> 3) < (1 << 28) / closeprob;
1312
1313         if (debugstart > 0 && testcalls >= debugstart)
1314                 debug = 1;
1315
1316         if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
1317                 prt("%lu...\n", testcalls);
1318
1319         offset = random();
1320         if (randomoplen)
1321                 size = random() % (maxoplen + 1);
1322
1323         /* calculate appropriate op to run */
1324         if (lite)
1325                 op = rv % OP_MAX_LITE;
1326         else
1327                 op = rv % OP_MAX_FULL;
1328
1329         switch (op) {
1330         case OP_MAPREAD:
1331                 if (!mapped_reads)
1332                         op = OP_READ;
1333                 break;
1334         case OP_MAPWRITE:
1335                 if (!mapped_writes)
1336                         op = OP_WRITE;
1337                 break;
1338         case OP_FALLOCATE:
1339                 if (!fallocate_calls) {
1340                         log4(OP_SKIPPED, OP_FALLOCATE, offset, size);
1341                         goto out;
1342                 }
1343                 break;
1344         case OP_PUNCH_HOLE:
1345                 if (!punch_hole_calls) {
1346                         log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, size);
1347                         goto out;
1348                 }
1349                 break;
1350         case OP_ZERO_RANGE:
1351                 if (!zero_range_calls) {
1352                         log4(OP_SKIPPED, OP_ZERO_RANGE, offset, size);
1353                         goto out;
1354                 }
1355                 break;
1356         }
1357
1358         switch (op) {
1359         case OP_READ:
1360                 TRIM_OFF_LEN(offset, size, file_size);
1361                 doread(offset, size);
1362                 break;
1363         case OP_WRITE:
1364                 TRIM_OFF_LEN(offset, size, maxfilelen);
1365                 dowrite(offset, size);
1366                 break;
1367         case OP_MAPREAD:
1368                 TRIM_OFF_LEN(offset, size, file_size);
1369                 domapread(offset, size);
1370                 break;
1371         case OP_MAPWRITE:
1372                 TRIM_OFF_LEN(offset, size, maxfilelen);
1373                 domapwrite(offset, size);
1374                 break;
1375         case OP_TRUNCATE:
1376                 if (!style)
1377                         size = random() % maxfilelen;
1378                 dotruncate(size);
1379                 break;
1380         case OP_FALLOCATE:
1381                 TRIM_OFF_LEN(offset, size, maxfilelen);
1382                 do_preallocate(offset, size);
1383                 break;
1384         case OP_PUNCH_HOLE:
1385                 TRIM_OFF_LEN(offset, size, file_size);
1386                 do_punch_hole(offset, size);
1387                 break;
1388         case OP_ZERO_RANGE:
1389                 TRIM_OFF_LEN(offset, size, file_size);
1390                 do_zero_range(offset, size);
1391                 break;
1392         case OP_CLOSEOPEN:
1393                 if (closeopen)
1394                         docloseopen();
1395                 break;
1396         default:
1397                 prterr("unknown operation %d: Operation not supported");
1398                 report_failure(42);
1399                 break;
1400         }
1401
1402 out:
1403         if (sizechecks && testcalls > simulatedopcount)
1404                 check_size();
1405 }
1406
1407 void
1408 segv(int sig)
1409 {
1410         if (jmpbuf_good) {
1411                 jmpbuf_good = 0;
1412                 longjmp(jmpbuf, 1);
1413         }
1414         report_failure(9999);
1415 }
1416
1417 void
1418 cleanup(sig)
1419         int     sig;
1420 {
1421         if (sig)
1422                 prt("signal %d\n", sig);
1423         prt("testcalls = %lu\n", testcalls);
1424         exit(sig);
1425 }
1426
1427 void
1428 usage(void)
1429 {
1430         fprintf(stdout,
1431                 "usage: fsx [-dfnqFLOW] [-b opnum] [-c Prob] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [ -I random|rotate ] [-N numops] [-P dirpath] [-S seed] [-Z [prob]] fname [additional paths to fname..]\n"
1432 "       -b opnum: beginning operation number (default 1)\n"
1433 "       -c P: 1 in P chance of file close+open at each op (default infinity)\n"
1434 "       -d: debug output for all operations [-d -d = more debugging]\n"
1435 "       -f flush and invalidate cache after I/O\n"
1436 /* OSX: -d duration: number of hours for the tool to run\n\ */
1437 /* OSX: -e: tests using an extended attribute rather than a file\n\ */
1438 /* OSX: -f forkname: test the named fork of fname\n\ */
1439 /* OSX: -g logpath: path for .fsxlog file\n\ */
1440 /* OSX: -h: write 0s instead of creating holes (i.e. sparse file)\n\ */
1441 /* OSX: -i: interactive mode, hit return before performing each operation\n\ */
1442 "       -l flen: the upper bound on file size (default 262144)\n"
1443 "       -m startop:endop: monitor (print debug output) specified byte range\n"
1444 "          (default 0:infinity)\n"
1445 "       -n: no verifications of file size\n"
1446 "       -o oplen: the upper bound on operation size (default 65536)\n"
1447 "       -p progressinterval: debug output at specified operation interval\n"
1448 "       -q: quieter operation\n"
1449 "       -r readbdy: %1$u would make reads page aligned (default 1)\n"
1450 "       -s style: 1 gives smaller truncates (default 0)\n"
1451 "       -t truncbdy: %1$u would make truncates page aligned (default 1)\n"
1452 "       -w writebdy: %1$u would make writes page aligned (default 1)\n"
1453 /* XFS: -x: preallocate file space before starting, XFS only (default 0)\n\ */
1454 "       -y synchronize changes to a file\n"
1455 /* OSX: -v: debug output for all operations\n\ */
1456 /* XFS: -A: Use the AIO system calls\n" */
1457 /* OSX: -C mix cached and un-cached read/write ops\n\ */
1458 "       -D startingop: debug output starting at specified operation\n"
1459 "       -F: Do not use fallocate (preallocation) calls\n"
1460 /* OSX: -G logsize: #entries in oplog (default 1024)\n\ */
1461 #ifdef FALLOC_FL_PUNCH_HOLE
1462 "       -H: Do not use punch hole calls\n"
1463 #endif
1464 #ifdef FALLOC_FL_ZERO_RANGE
1465 "       -z: Do not use zero range calls\n"
1466 #endif
1467 /* XFS: -C: Do not use collapse range calls\n\ */
1468 "       -I [rotate|random]: When multiple paths to the file are given,\n"
1469 "           each operation uses a different path.  Iterate through them in\n"
1470 "           order with 'rotate' or chose them at 'random'.  (default random)\n"
1471 "       -L: fsxLite - no file creations & no file size changes\n"
1472 /* OSX: -I: start interactive mode since operation opnum\n\ */
1473 /* OSX: -M: slow motion mode, wait 1 second before each op\n\ */
1474 "       -N numops: total # operations to do (default infinity)\n"
1475 "       -O: use oplen (see -o flag) for every op (default random)\n"
1476 "       -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n"
1477 "       -R: read() system calls only (mapped reads disabled)\n"
1478 "       -S seed: for random # generator (default 1) 0 gets timestamp\n"
1479 /* OSX: -T datasize: atomic data element write size [1,2,4] (default 4)\n\ */
1480 "       -W: mapped write operations DISabled\n"
1481 #ifdef O_DIRECT
1482 "       -Z[P]: O_DIRECT file IO [1 in P chance for each open] (default off)\n"
1483 #endif
1484 "       fname: this filename is REQUIRED (no default)\n",
1485         page_size);
1486         exit(90);
1487 }
1488
1489 int
1490 getnum(char *s, char **e)
1491 {
1492         int ret = -1;
1493
1494         *e = (char *)0;
1495         ret = strtol(s, e, 0);
1496         if (*e)
1497                 switch (**e) {
1498                 case 'b':
1499                 case 'B':
1500                         ret *= 512;
1501                         *e = *e + 1;
1502                         break;
1503                 case 'k':
1504                 case 'K':
1505                         ret *= 1024;
1506                         *e = *e + 1;
1507                         break;
1508                 case 'm':
1509                 case 'M':
1510                         ret *= 1024 * 1024;
1511                         *e = *e + 1;
1512                         break;
1513                 case 'w':
1514                 case 'W':
1515                         ret *= 4;
1516                         *e = *e + 1;
1517                         break;
1518                 }
1519         return (ret);
1520 }
1521
1522 int
1523 test_fallocate(int mode)
1524 {
1525         int ret = 0;
1526         int fd = get_fd();
1527
1528         if (!lite) {
1529                 /* Must go more than a page away so let's go 4M to be sure */
1530                 if (fallocate(fd, mode, 0, 4096*1024) && errno == EOPNOTSUPP) {
1531                         if (!quiet)
1532                                 warn("%s: filesystem does not support fallocate mode 0x%x, disabling!",
1533                                      __func__, mode);
1534                 } else {
1535                         ret = 1;
1536                 }
1537
1538                 /* Always call ftruncate since file size might be adjusted
1539                  * by fallocate even on error
1540                  */
1541                 if (ftruncate(fd, 0) == -1)
1542                         warn("ftruncate to 0 size failed");
1543         }
1544         return ret;
1545 }
1546
1547 int
1548 main(int argc, char **argv)
1549 {
1550         int i, style, ch;
1551         char *endp;
1552         int dirpath = 0;
1553
1554         goodfile[0] = 0;
1555         logfile[0] = 0;
1556
1557         page_size = getpagesize();
1558         page_mask = page_size - 1;
1559
1560         setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
1561
1562         while ((ch = getopt(argc, argv,
1563                             "b:c:dfl:m:no:p:qr:s:t:w:xyzD:FHI:LN:OP:RS:WZ::"))
1564                != EOF)
1565                 switch (ch) {
1566                 case 'b':
1567                         simulatedopcount = getnum(optarg, &endp);
1568                         if (!quiet)
1569                                 fprintf(stdout, "Will begin at operation %ld\n",
1570                                         simulatedopcount);
1571                         if (simulatedopcount == 0)
1572                                 usage();
1573                         simulatedopcount -= 1;
1574                         break;
1575                 case 'c':
1576                         closeprob = getnum(optarg, &endp);
1577                         if (!quiet)
1578                                 fprintf(stdout,
1579                                         "Chance of close/open is 1 in %d\n",
1580                                         closeprob);
1581                         if (closeprob <= 0)
1582                                 usage();
1583                         break;
1584                 case 'd':
1585                         debug++;
1586                         break;
1587                 case 'f':
1588                         flush = 1;
1589                         break;
1590                 case 'l':
1591                         maxfilelen = getnum(optarg, &endp);
1592                         if (maxfilelen <= 0)
1593                                 usage();
1594                         break;
1595                 case 'm':
1596                         monitorstart = getnum(optarg, &endp);
1597                         if (monitorstart < 0)
1598                                 usage();
1599                         if (!endp || *endp++ != ':')
1600                                 usage();
1601                         monitorend = getnum(endp, &endp);
1602                         if (monitorend < 0)
1603                                 usage();
1604                         if (monitorend == 0)
1605                                 monitorend = -1; /* aka infinity */
1606                         debug = 1;
1607                 case 'n':
1608                         sizechecks = 0;
1609                         break;
1610                 case 'o':
1611                         maxoplen = getnum(optarg, &endp);
1612                         if (maxoplen <= 0)
1613                                 usage();
1614                         break;
1615                 case 'p':
1616                         progressinterval = getnum(optarg, &endp);
1617                         if (progressinterval <= 0)
1618                                 usage();
1619                         break;
1620                 case 'q':
1621                         quiet = 1;
1622                         break;
1623                 case 'r':
1624                         readbdy = getnum(optarg, &endp);
1625                         if (readbdy <= 0)
1626                                 usage();
1627                         break;
1628                 case 's':
1629                         style = getnum(optarg, &endp);
1630                         if (style < 0 || style > 1)
1631                                 usage();
1632                         break;
1633                 case 't':
1634                         truncbdy = getnum(optarg, &endp);
1635                         if (truncbdy <= 0)
1636                                 usage();
1637                         break;
1638                 case 'w':
1639                         writebdy = getnum(optarg, &endp);
1640                         if (writebdy <= 0)
1641                                 usage();
1642                         break;
1643                 case 'y':
1644                         do_fsync = 1;
1645                         break;
1646                 case 'D':
1647                         debugstart = getnum(optarg, &endp);
1648                         if (debugstart < 1)
1649                                 usage();
1650                         break;
1651                 case 'F':
1652                         fallocate_calls = 0;
1653                         break;
1654                 case 'H':
1655                         punch_hole_calls = 0;
1656                         break;
1657                 case 'z':
1658                         zero_range_calls = 0;
1659                         break;
1660                 case 'I':
1661                         assign_fd_policy(optarg);
1662                         break;
1663                 case 'L':
1664                         lite = 1;
1665                         break;
1666                 case 'N':
1667                         numops = getnum(optarg, &endp);
1668                         if (numops < 0)
1669                                 usage();
1670                         break;
1671                 case 'O':
1672                         randomoplen = 0;
1673                         break;
1674                 case 'P':
1675                         strncpy(goodfile, optarg, sizeof(goodfile) - 1);
1676                         strncat(goodfile, "/", PATH_MAX - strlen(goodfile) - 1);
1677                         strncpy(logfile, optarg, sizeof(logfile) - 1);
1678                         strncat(logfile, "/", PATH_MAX - strlen(logfile) - 1);
1679                         dirpath = 1;
1680                         break;
1681                 case 'R':
1682                         mapped_reads = 0;
1683                         break;
1684                 case 'S':
1685                         seed = getnum(optarg, &endp);
1686                         if (seed == 0)
1687                                 seed = time(0) % 10000;
1688                         if (!quiet)
1689                                 fprintf(stdout, "Seed set to %d\n", seed);
1690                         if (seed < 0)
1691                                 usage();
1692                         break;
1693                 case 'W':
1694                         mapped_writes = 0;
1695                         if (!quiet)
1696                                 fprintf(stdout, "mapped writes DISABLED\n");
1697                         break;
1698                 case 'Z':
1699 #ifdef O_DIRECT
1700                         if (optarg)
1701                                 o_direct = getnum(optarg, &endp);
1702                         if (!optarg || o_direct == 0)
1703                                 o_direct = 1;
1704 #endif
1705                         break;
1706                 default:
1707                         usage();
1708                         /* NOTREACHED */
1709                 }
1710         argc -= optind;
1711         argv += optind;
1712         if (argc < 1)
1713                 usage();
1714         fname = argv[0];
1715
1716         signal(SIGHUP, cleanup);
1717         signal(SIGINT, cleanup);
1718         signal(SIGPIPE, cleanup);
1719         signal(SIGALRM, cleanup);
1720         signal(SIGTERM, cleanup);
1721         signal(SIGXCPU, cleanup);
1722         signal(SIGXFSZ, cleanup);
1723         signal(SIGVTALRM, cleanup);
1724         signal(SIGUSR1, cleanup);
1725         signal(SIGUSR2, cleanup);
1726         signal(SIGBUS, segv);
1727         signal(SIGSEGV, segv);
1728
1729         initstate(seed, state, 256);
1730         setstate(state);
1731
1732         open_test_files(argv, argc);
1733
1734         strncat(goodfile, dirpath ? my_basename(fname) : fname, 256);
1735         strncat(goodfile, ".fsxgood", PATH_MAX - strlen(goodfile) - 1);
1736         fsxgoodfd = open(goodfile, O_RDWR | O_CREAT | O_TRUNC, 0666);
1737         if (fsxgoodfd < 0) {
1738                 prterr(goodfile);
1739                 exit(92);
1740         }
1741         strncat(logfile, dirpath ? my_basename(fname) : fname, 256);
1742         strncat(logfile, ".fsxlog", PATH_MAX - strlen(logfile) - 1);
1743         fsxlogf = fopen(logfile, "w");
1744         if (!fsxlogf) {
1745                 prterr(logfile);
1746                 exit(93);
1747         }
1748         if (lite) {
1749                 off_t ret;
1750                 int fd = get_fd();
1751
1752                 maxfilelen = lseek(fd, (off_t)0, SEEK_END);
1753                 file_size = maxfilelen;
1754                 if (file_size == (off_t)-1) {
1755                         prterr(fname);
1756                         warn("%s: lseek eof", __func__);
1757                         exit(94);
1758                 }
1759                 ret = lseek(fd, (off_t)0, SEEK_SET);
1760                 if (ret == (off_t)-1) {
1761                         prterr(fname);
1762                         warn("%s: lseek 0", __func__);
1763                         exit(95);
1764                 }
1765         }
1766         original_buf = (char *)malloc(maxfilelen);
1767         if (!original_buf)
1768                 exit(96);
1769         for (i = 0; i < maxfilelen; i++)
1770                 original_buf[i] = random() % 256;
1771         if (o_direct) {
1772                 int ret;
1773
1774                 ret = posix_memalign((void **)&good_buf, writebdy, maxfilelen);
1775                 if (ret) {
1776                         prt("%s: posix_memalign failed: %s\n", __func__,
1777                             strerror(ret));
1778                         exit(96);
1779                 }
1780
1781                 ret = posix_memalign((void **)&temp_buf, readbdy, maxoplen);
1782                 if (ret) {
1783                         prt("%s: posix_memalign failed: %s\n", __func__,
1784                             strerror(ret));
1785                         exit(97);
1786                 }
1787         } else {
1788                 good_buf = malloc(maxfilelen);
1789                 if (!good_buf) {
1790                         prt("malloc failed.\n");
1791                         exit(98);
1792                 }
1793
1794                 temp_buf = malloc(maxoplen);
1795                 if (!temp_buf) {
1796                         prt("malloc failed.\n");
1797                         exit(99);
1798                 }
1799         }
1800         memset(good_buf, 0, maxfilelen);
1801         memset(temp_buf, 0, maxoplen);
1802
1803         if (lite) {     /* zero entire existing file */
1804                 ssize_t written;
1805                 int fd = get_fd();
1806
1807                 written = write(fd, good_buf, (size_t)maxfilelen);
1808                 if (written != maxfilelen) {
1809                         if (written == -1) {
1810                                 prterr(fname);
1811                                 warn("%s: error on write", __func__);
1812                         } else {
1813                                 warn("%s: short write, 0x%x bytes instead of 0x%lx\n",
1814                                      __func__, (unsigned int)written,
1815                                      maxfilelen);
1816                         }
1817                         exit(98);
1818                 }
1819         } else {
1820                 check_trunc_hack();
1821         }
1822
1823         if (fallocate_calls)
1824                 fallocate_calls = test_fallocate(0);
1825
1826         if (punch_hole_calls)
1827                 punch_hole_calls = test_fallocate(FALLOC_FL_PUNCH_HOLE |
1828                                                   FALLOC_FL_KEEP_SIZE);
1829
1830         if (zero_range_calls)
1831                 zero_range_calls = test_fallocate(FALLOC_FL_ZERO_RANGE);
1832
1833         fl_keep_size = test_fallocate(FALLOC_FL_KEEP_SIZE);
1834
1835         while (numops == -1 || numops--)
1836                 test();
1837
1838         close_test_files();
1839         prt("All operations completed A-OK!\n");
1840
1841         free(original_buf);
1842         free(good_buf);
1843         free(temp_buf);
1844
1845         return 0;
1846 }