Whamcloud - gitweb
LU-1866 osd: ancillary work for initial OI scrub
[fs/lustre-release.git] / lustre / tests / fsx.c
1 /*
2  * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_LICENSE_HEADER_START@
5  *
6  * The contents of this file constitute Original Code as defined in and
7  * are subject to the Apple Public Source License Version 1.1 (the
8  * "License").  You may not use this file except in compliance with the
9  * License.  Please obtain a copy of the License at
10  * http://www.apple.com/publicsource and read it before using this file.
11  *
12  * This Original Code and all software distributed under the License are
13  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17  * License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * @APPLE_LICENSE_HEADER_END@
21  *
22  *      File:   fsx.c
23  *      Author: Avadis Tevanian, Jr.
24  *
25  *      File system exerciser.
26  *
27  *      Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com
28  *
29  *      Various features from Joe Sokol, Pat Dirks, and Clark Warner.
30  *
31  *      Small changes to work under Linux -- davej@suse.de
32  *
33  *      Sundry porting patches from Guy Harris 12/2001
34  * $FreeBSD: src/tools/regression/fsx/fsx.c,v 1.1 2001/12/20 04:15:57 jkh Exp $
35  */
36
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #if defined(_UWIN) || defined(__linux__)
40 # include <sys/param.h>
41 # include <limits.h>
42 # include <time.h>
43 # include <strings.h>
44 # include <sys/time.h>
45 #endif
46 #include <fcntl.h>
47 #include <sys/mman.h>
48 #ifndef MAP_FILE
49 # define MAP_FILE 0
50 #endif
51 #include <limits.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <stdarg.h>
58 #include <errno.h>
59
60 #define NUMPRINTCOLUMNS 32      /* # columns of data to print on each line */
61
62 /*
63  * Each test run will work with one or more separate file descriptors for the
64  * same file.  This allows testing cache coherency across multiple mountpoints
65  * of the same network filesystem on a single client.
66  */
67 struct test_file {
68         char *path;
69         int fd;
70 } *test_files = NULL;
71
72 int num_test_files;
73 enum fd_iteration_policy {
74         FD_SINGLE,
75         FD_ROTATE,
76         FD_RANDOM,
77 };
78 int fd_policy = FD_RANDOM;
79 int fd_last;
80
81 /*
82  *      A log entry is an operation and a bunch of arguments.
83  */
84
85 struct log_entry {
86         int                     operation;
87         int                     args[3];
88         struct timeval          tv;
89         const struct test_file  *tf;
90 };
91
92 #define LOGSIZE 100000
93
94 struct log_entry        oplog[LOGSIZE]; /* the log */
95 int                     logptr = 0;     /* current position in log */
96 int                     logcount = 0;   /* total ops */
97
98 /*
99  *      Define operations
100  */
101
102 #define OP_READ         1
103 #define OP_WRITE        2
104 #define OP_TRUNCATE     3
105 #define OP_CLOSEOPEN    4
106 #define OP_MAPREAD      5
107 #define OP_MAPWRITE     6
108 #define OP_SKIPPED      7
109
110 int page_size;
111 int page_mask;
112
113 char    *original_buf;                  /* a pointer to the original data */
114 char    *good_buf;                      /* a pointer to the correct data */
115 char    *temp_buf;                      /* a pointer to the current data */
116 char    *fname;                         /* name of our test file */
117 char    logfile[1024];                  /* name of our log file */
118 char    goodfile[1024];                 /* name of our test file */
119
120 off_t           file_size = 0;
121 off_t           biggest = 0;
122 char            state[256];
123 unsigned long   testcalls = 0;          /* calls to function "test" */
124
125 unsigned long   simulatedopcount = 0;   /* -b flag */
126 int     closeprob = 0;                  /* -c flag */
127 int     debug = 0;                      /* -d flag */
128 unsigned long   debugstart = 0;         /* -D flag */
129 unsigned long   maxfilelen = 256 * 1024;        /* -l flag */
130 int     sizechecks = 1;                 /* -n flag disables them */
131 int     maxoplen = 64 * 1024;           /* -o flag */
132 int     quiet = 0;                      /* -q flag */
133 unsigned long progressinterval = 0;     /* -p flag */
134 int     readbdy = 1;                    /* -r flag */
135 int     style = 0;                      /* -s flag */
136 int     truncbdy = 1;                   /* -t flag */
137 int     writebdy = 1;                   /* -w flag */
138 long    monitorstart = -1;              /* -m flag */
139 long    monitorend = -1;                /* -m flag */
140 int     lite = 0;                       /* -L flag */
141 long    numops = -1;                    /* -N flag */
142 int     randomoplen = 1;                /* -O flag disables it */
143 int     seed = 1;                       /* -S flag */
144 int     mapped_writes = 1;              /* -W flag disables */
145 int     mapped_reads = 1;               /* -R flag disables it */
146 int     fsxgoodfd = 0;
147 FILE *  fsxlogf = NULL;
148 int badoff = -1;
149
150
151 void
152 vwarnc(code, fmt, ap)
153         int code;
154         const char *fmt;
155         va_list ap;
156 {
157         fprintf(stderr, "fsx: ");
158         if (fmt != NULL) {
159                 vfprintf(stderr, fmt, ap);
160                 fprintf(stderr, ": ");
161         }
162         fprintf(stderr, "%s\n", strerror(code));
163 }
164
165
166 void
167 warn(const char * fmt, ...)
168 {
169         va_list ap;
170         va_start(ap, fmt);
171         vwarnc(errno, fmt, ap);
172         va_end(ap);
173 }
174
175
176 void
177 __attribute__((format(printf, 1, 2)))
178 prt(char *fmt, ...)
179 {
180         va_list args;
181
182         va_start(args, fmt);
183         vfprintf(stdout, fmt, args);
184         va_end(args);
185
186         if (fsxlogf) {
187                 va_start(args, fmt);
188                 vfprintf(fsxlogf, fmt, args);
189                 va_end(args);
190         }
191 }
192
193 void
194 prterr(char *prefix)
195 {
196         prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
197 }
198
199
200 void
201 log4(int operation, int arg0, int arg1, int arg2, struct timeval *tv,
202         const struct test_file *tf)
203 {
204         struct log_entry *le;
205
206         le = &oplog[logptr];
207         le->operation = operation;
208         le->args[0] = arg0;
209         le->args[1] = arg1;
210         le->args[2] = arg2;
211         le->tv = *tv;
212         le->tf = tf;
213         logptr++;
214         logcount++;
215         if (logptr >= LOGSIZE)
216                 logptr = 0;
217 }
218
219 const char *
220 fill_tf_buf(const struct test_file *tf)
221 {
222         static int max_tf_len;
223         static char tf_buf[32];
224
225         if (fd_policy == FD_SINGLE)
226                 return "";
227
228         if (max_tf_len == 0)
229                 max_tf_len = snprintf(tf_buf, sizeof(tf_buf) - 1,
230                                       "%u", num_test_files - 1);
231
232         sprintf(tf_buf, "[%0*lu]", max_tf_len,
233                 (unsigned long)(tf - test_files));
234
235         return tf_buf;
236 }
237
238 void
239 logdump(void)
240 {
241         int     i, count, down;
242         struct log_entry        *lp;
243
244         prt("LOG DUMP (%d total operations):\n", logcount);
245         if (logcount < LOGSIZE) {
246                 i = 0;
247                 count = logcount;
248         } else {
249                 i = logptr;
250                 count = LOGSIZE;
251         }
252         for ( ; count > 0; count--) {
253                 int opnum;
254
255                 opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE;
256                 lp = &oplog[i];
257                 prt("%d%s: %lu.%06lu ", opnum, fill_tf_buf(lp->tf),
258                     lp->tv.tv_sec, lp->tv.tv_usec);
259
260                 switch (lp->operation) {
261                 case OP_MAPREAD:
262                         prt("MAPREAD  0x%x thru 0x%x (0x%x bytes)",
263                             lp->args[0], lp->args[0] + lp->args[1] - 1,
264                             lp->args[1]);
265                         if (badoff >= lp->args[0] && badoff <
266                                                      lp->args[0] + lp->args[1])
267                                 prt("\t***RRRR***");
268                         break;
269                 case OP_MAPWRITE:
270                         prt("MAPWRITE 0x%x thru 0x%x (0x%x bytes)",
271                             lp->args[0], lp->args[0] + lp->args[1] - 1,
272                             lp->args[1]);
273                         if (badoff >= lp->args[0] && badoff <
274                                                      lp->args[0] + lp->args[1])
275                                 prt("\t******WWWW");
276                         break;
277                 case OP_READ:
278                         prt("READ     0x%x thru 0x%x (0x%x bytes)",
279                             lp->args[0], lp->args[0] + lp->args[1] - 1,
280                             lp->args[1]);
281                         if (badoff >= lp->args[0] &&
282                             badoff < lp->args[0] + lp->args[1])
283                                 prt("\t***RRRR***");
284                         break;
285                 case OP_WRITE:
286                         prt("WRITE    0x%x thru 0x%x (0x%x bytes)",
287                             lp->args[0], lp->args[0] + lp->args[1] - 1,
288                             lp->args[1]);
289                         if (lp->args[0] > lp->args[2])
290                                 prt(" HOLE");
291                         else if (lp->args[0] + lp->args[1] > lp->args[2])
292                                 prt(" EXTEND");
293                         if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
294                             badoff < lp->args[0] + lp->args[1])
295                                 prt("\t***WWWW");
296                         break;
297                 case OP_TRUNCATE:
298                         down = lp->args[0] < lp->args[1];
299                         prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
300                             down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
301                         if (badoff >= lp->args[!down] &&
302                             badoff < lp->args[!!down])
303                                 prt("\t******WWWW");
304                         break;
305                 case OP_CLOSEOPEN:
306                         prt("CLOSE/OPEN");
307                         break;
308                 case OP_SKIPPED:
309                         prt("SKIPPED (no operation)");
310                         break;
311                 default:
312                         prt("BOGUS LOG ENTRY (operation code = %d)!",
313                             lp->operation);
314                 }
315                 prt("\n");
316                 i++;
317                 if (i == LOGSIZE)
318                         i = 0;
319         }
320 }
321
322
323 void
324 save_buffer(char *buffer, off_t bufferlength, int fd)
325 {
326         off_t ret;
327         ssize_t byteswritten;
328
329         if (fd <= 0 || bufferlength == 0)
330                 return;
331
332         if (bufferlength > SSIZE_MAX) {
333                 prt("fsx flaw: overflow in save_buffer\n");
334                 exit(67);
335         }
336         if (lite) {
337                 off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END);
338                 if (size_by_seek == (off_t)-1)
339                         prterr("save_buffer: lseek eof");
340                 else if (bufferlength > size_by_seek) {
341                         warn("save_buffer: .fsxgood file too short... will"
342                                 "save 0x%llx bytes instead of 0x%llx\n",
343                                 (unsigned long long)size_by_seek,
344                                 (unsigned long long)bufferlength);
345                         bufferlength = size_by_seek;
346                 }
347         }
348
349         ret = lseek(fd, (off_t)0, SEEK_SET);
350         if (ret == (off_t)-1)
351                 prterr("save_buffer: lseek 0");
352
353         byteswritten = write(fd, buffer, (size_t)bufferlength);
354         if (byteswritten != bufferlength) {
355                 if (byteswritten == -1)
356                         prterr("save_buffer write");
357                 else
358                         warn("save_buffer: short write, 0x%x bytes instead"
359                                 "of 0x%llx\n",
360                              (unsigned)byteswritten,
361                              (unsigned long long)bufferlength);
362         }
363 }
364
365
366 void
367 report_failure(int status)
368 {
369         logdump();
370
371         if (fsxgoodfd) {
372                 if (good_buf) {
373                         save_buffer(good_buf, file_size, fsxgoodfd);
374                         prt("Correct content saved for comparison\n");
375                         prt("(maybe hexdump \"%s\" vs \"%s\")\n",
376                             fname, goodfile);
377                 }
378                 close(fsxgoodfd);
379         }
380         exit(status);
381 }
382
383
384 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
385                                         *(((unsigned char *)(cp)) + 1)))
386
387 void
388 check_buffers(unsigned offset, unsigned size)
389 {
390         unsigned char c, t;
391         unsigned i = 0;
392         unsigned n = 0;
393         unsigned op = 0;
394         unsigned bad = 0;
395
396         if (memcmp(good_buf + offset, temp_buf, size) != 0) {
397                 prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n",
398                     offset, size);
399                 prt("OFFSET\tGOOD\tBAD\tRANGE\n");
400                 while (size > 0) {
401                         c = good_buf[offset];
402                         t = temp_buf[i];
403                         if (c != t) {
404                                 if (n == 0) {
405                                         bad = short_at(&temp_buf[i]);
406                                         prt("%#07x\t%#06x\t%#06x", offset,
407                                             short_at(&good_buf[offset]), bad);
408                                         op = temp_buf[offset & 1 ? i+1 : i];
409                                 }
410                                 n++;
411                                 badoff = offset;
412                         }
413                         offset++;
414                         i++;
415                         size--;
416                 }
417                 if (n) {
418                         prt("\t%#7x\n", n);
419                         if (bad)
420                                 prt("operation# (mod 256) for the bad data"
421                                         "may be %u\n", ((unsigned)op & 0xff));
422                         else
423                                 prt("operation# (mod 256) for the bad data"
424                                         "unknown, check HOLE and EXTEND ops\n");
425                 } else
426                         prt("????????????????\n");
427                 report_failure(110);
428         }
429 }
430
431 struct test_file *
432 get_tf(void)
433 {
434         unsigned index = 0;
435
436         switch (fd_policy) {
437                 case FD_ROTATE:
438                         index = fd_last++;
439                         break;
440                 case FD_RANDOM:
441                         index = random();
442                         break;
443                 case FD_SINGLE:
444                         index = 0;
445                         break;
446                 default:
447                         prt("unknown policy");
448                         exit(1);
449                         break;
450         }
451         return &test_files[ index % num_test_files ];
452 }
453
454 void
455 assign_fd_policy(char *policy)
456 {
457         if (!strcmp(policy, "random"))
458                 fd_policy = FD_RANDOM;
459         else if (!strcmp(policy, "rotate"))
460                 fd_policy = FD_ROTATE;
461         else {
462                 prt("unknown -I policy: '%s'\n", policy);
463                 exit(1);
464         }
465 }
466
467 int
468 get_fd(void)
469 {
470         struct test_file *tf = get_tf();
471         return tf->fd;
472 }
473
474 static const char *my_basename(const char *path)
475 {
476         char *c = strrchr(path, '/');
477
478         return c ? c++ : path;
479 }
480
481 void
482 open_test_files(char **argv, int argc)
483 {
484         struct test_file *tf;
485         int i;
486
487         num_test_files = argc;
488         if (num_test_files == 1)
489                 fd_policy = FD_SINGLE;
490
491         test_files = calloc(num_test_files, sizeof(*test_files));
492         if (test_files == NULL) {
493                 prterr("reallocating space for test files");
494                 exit(1);
495         }
496
497         for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
498
499                 tf->path = argv[i];
500                 tf->fd = open(tf->path, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC),
501                                 0666);
502                 if (tf->fd < 0) {
503                         prterr(tf->path);
504                         exit(91);
505                 }
506         }
507
508         if (quiet || fd_policy == FD_SINGLE)
509                 return;
510
511         for (i = 0, tf = test_files; i < num_test_files; i++, tf++)
512                 prt("fd %d: %s\n", i, tf->path);
513 }
514
515 void
516 close_test_files(void)
517 {
518         int i;
519         struct test_file *tf;
520
521         for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
522                 if (close(tf->fd)) {
523                         prterr("close");
524                         report_failure(99);
525                 }
526         }
527 }
528
529
530 void
531 check_size(void)
532 {
533         struct stat     statbuf;
534         off_t   size_by_seek;
535         int fd = get_fd();
536
537         if (fstat(fd, &statbuf)) {
538                 prterr("check_size: fstat");
539                 statbuf.st_size = -1;
540         }
541         size_by_seek = lseek(fd, (off_t)0, SEEK_END);
542         if (file_size != statbuf.st_size || file_size != size_by_seek) {
543                 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
544                     (unsigned long long)file_size,
545                     (unsigned long long)statbuf.st_size,
546                     (unsigned long long)size_by_seek);
547                 report_failure(120);
548         }
549 }
550
551
552 void
553 check_trunc_hack(void)
554 {
555         struct stat statbuf;
556         int fd = get_fd();
557
558         /* should not ignore ftruncate(2)'s return value */
559         if (ftruncate(fd, (off_t)0) < 0) {
560                 prterr("trunc_hack: ftruncate(0)");
561                 exit(1);
562         }
563         if (ftruncate(fd, (off_t)100000) < 0) {
564                 prterr("trunc_hack: ftruncate(100000)");
565                 exit(1);
566         }
567         if (fstat(fd, &statbuf)) {
568                 prterr("trunc_hack: fstat");
569                 statbuf.st_size = -1;
570         }
571         if (statbuf.st_size != (off_t)100000) {
572                 prt("no extend on truncate! not posix!\n");
573                 exit(130);
574         }
575         if (ftruncate(fd, 0) < 0) {
576                 prterr("trunc_hack: ftruncate(0) (2nd call)");
577                 exit(1);
578         }
579 }
580
581 void
582 output_line(struct test_file *tf, int op, unsigned offset,
583                 unsigned size, struct timeval *tv)
584 {
585         char *ops[] = {
586                 [OP_READ] = "read",
587                 [OP_WRITE] = "write",
588                 [OP_TRUNCATE] = "trunc from",
589                 [OP_MAPREAD] = "mapread",
590                 [OP_MAPWRITE] = "mapwrite",
591         };
592
593         /* W. */
594         if (!(!quiet && ((progressinterval &&
595                         testcalls % progressinterval == 0) ||
596                        (debug &&
597                         (monitorstart == -1 ||
598                          (offset + size > monitorstart &&
599                           (monitorend == -1 || offset <= monitorend)))))))
600                 return;
601
602         prt("%06lu%s %lu.%06lu %-10s %#08x %s %#08x\t(0x%x bytes)\n",
603                 testcalls, fill_tf_buf(tf), tv->tv_sec, tv->tv_usec,
604                 ops[op],
605                 offset, op == OP_TRUNCATE ? " to " : "thru",
606                 offset + size - 1, size);
607 }
608
609 void
610 doread(unsigned offset, unsigned size)
611 {
612         struct timeval t;
613         off_t ret;
614         unsigned iret;
615         struct test_file *tf = get_tf();
616         int fd = tf->fd;
617
618         offset -= offset % readbdy;
619         gettimeofday(&t, NULL);
620         if (size == 0) {
621                 if (!quiet && testcalls > simulatedopcount)
622                         prt("skipping zero size read\n");
623                 log4(OP_SKIPPED, OP_READ, offset, size, &t, tf);
624                 return;
625         }
626         if (size + offset > file_size) {
627                 if (!quiet && testcalls > simulatedopcount)
628                         prt("skipping seek/read past end of file\n");
629                 log4(OP_SKIPPED, OP_READ, offset, size, &t, tf);
630                 return;
631         }
632
633         log4(OP_READ, offset, size, 0, &t, tf);
634
635         if (testcalls <= simulatedopcount)
636                 return;
637
638         output_line(tf, OP_READ, offset, size, &t);
639
640         ret = lseek(fd, (off_t)offset, SEEK_SET);
641         if (ret == (off_t)-1) {
642                 prterr("doread: lseek");
643                 report_failure(140);
644         }
645         iret = read(fd, temp_buf, size);
646         if (!quiet && (debug > 1 &&
647                         (monitorstart == -1 ||
648                          (offset + size > monitorstart &&
649                           (monitorend == -1 || offset <= monitorend))))) {
650                 gettimeofday(&t, NULL);
651                 prt("       %lu.%06lu read done\n", t.tv_sec, t.tv_usec);
652         }
653         if (iret != size) {
654                 if (iret == -1)
655                         prterr("doread: read");
656                 else
657                         prt("short read: 0x%x bytes instead of 0x%x\n",
658                             iret, size);
659                 report_failure(141);
660         }
661         check_buffers(offset, size);
662 }
663
664
665 void
666 domapread(unsigned offset, unsigned size)
667 {
668         struct timeval t;
669         unsigned pg_offset;
670         unsigned map_size;
671         char    *p;
672         struct test_file *tf = get_tf();
673         int fd = tf->fd;
674
675         offset -= offset % readbdy;
676         gettimeofday(&t, NULL);
677         if (size == 0) {
678                 if (!quiet && testcalls > simulatedopcount)
679                         prt("skipping zero size read\n");
680                 log4(OP_SKIPPED, OP_MAPREAD, offset, size, &t, tf);
681                 return;
682         }
683         if (size + offset > file_size) {
684                 if (!quiet && testcalls > simulatedopcount)
685                         prt("skipping seek/read past end of file\n");
686                 log4(OP_SKIPPED, OP_MAPREAD, offset, size, &t, tf);
687                 return;
688         }
689
690         log4(OP_MAPREAD, offset, size, 0, &t, tf);
691
692         if (testcalls <= simulatedopcount)
693                 return;
694
695         output_line(tf, OP_MAPREAD, offset, size, &t);
696
697         pg_offset = offset & page_mask;
698         map_size  = pg_offset + size;
699
700         if ((p = mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd,
701                       (off_t)(offset - pg_offset))) == MAP_FAILED) {
702                 prterr("domapread: mmap");
703                 report_failure(190);
704         }
705         if (!quiet && (debug > 1 &&
706                         (monitorstart == -1 ||
707                          (offset + size > monitorstart &&
708                           (monitorend == -1 || offset <= monitorend))))) {
709                 gettimeofday(&t, NULL);
710                 prt("       %lu.%06lu mmap done\n", t.tv_sec, t.tv_usec);
711         }
712         memcpy(temp_buf, p + pg_offset, size);
713         if (!quiet && (debug > 1 &&
714                         (monitorstart == -1 ||
715                          (offset + size > monitorstart &&
716                           (monitorend == -1 || offset <= monitorend))))) {
717                 gettimeofday(&t, NULL);
718                 prt("       %lu.%06lu memcpy done\n", t.tv_sec, t.tv_usec);
719         }
720         if (munmap(p, map_size) != 0) {
721                 prterr("domapread: munmap");
722                 report_failure(191);
723         }
724         if (!quiet && (debug > 1 &&
725                         (monitorstart == -1 ||
726                          (offset + size > monitorstart &&
727                           (monitorend == -1 || offset <= monitorend))))) {
728                 gettimeofday(&t, NULL);
729                 prt("       %lu.%06lu munmap done\n", t.tv_sec, t.tv_usec);
730         }
731
732         check_buffers(offset, size);
733 }
734
735
736 void
737 gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
738 {
739         while (size--) {
740                 good_buf[offset] = testcalls % 256;
741                 if (offset % 2)
742                         good_buf[offset] += original_buf[offset];
743                 offset++;
744         }
745 }
746
747
748 void
749 dowrite(unsigned offset, unsigned size)
750 {
751         struct timeval t;
752         off_t ret;
753         unsigned iret;
754         struct test_file *tf = get_tf();
755         int fd = tf->fd;
756
757         offset -= offset % writebdy;
758         gettimeofday(&t, NULL);
759         if (size == 0) {
760                 if (!quiet && testcalls > simulatedopcount)
761                         prt("skipping zero size write\n");
762                 log4(OP_SKIPPED, OP_WRITE, offset, size, &t, tf);
763                 return;
764         }
765
766         log4(OP_WRITE, offset, size, file_size, &t, tf);
767
768         gendata(original_buf, good_buf, offset, size);
769         if (file_size < offset + size) {
770                 if (file_size < offset)
771                         memset(good_buf + file_size, '\0', offset - file_size);
772                 file_size = offset + size;
773                 if (lite) {
774                         warn("Lite file size bug in fsx!");
775                         report_failure(149);
776                 }
777         }
778
779         if (testcalls <= simulatedopcount)
780                 return;
781
782         output_line(tf, OP_WRITE, offset, size, &t);
783
784         ret = lseek(fd, (off_t)offset, SEEK_SET);
785         if (ret == (off_t)-1) {
786                 prterr("dowrite: lseek");
787                 report_failure(150);
788         }
789         iret = write(fd, good_buf + offset, size);
790         if (!quiet && (debug > 1 &&
791                         (monitorstart == -1 ||
792                          (offset + size > monitorstart &&
793                           (monitorend == -1 || offset <= monitorend))))) {
794                 gettimeofday(&t, NULL);
795                 prt("       %lu.%06lu write done\n", t.tv_sec, t.tv_usec);
796         }
797         if (iret != size) {
798                 if (iret == -1)
799                         prterr("dowrite: write");
800                 else
801                         prt("short write: 0x%x bytes instead of 0x%x\n",
802                             iret, size);
803                 report_failure(151);
804         }
805 }
806
807
808 void
809 domapwrite(unsigned offset, unsigned size)
810 {
811         struct timeval t;
812         unsigned pg_offset;
813         unsigned map_size;
814         off_t    cur_filesize;
815         char    *p;
816         struct test_file *tf = get_tf();
817         int fd = tf->fd;
818
819         offset -= offset % writebdy;
820         gettimeofday(&t, NULL);
821         if (size == 0) {
822                 if (!quiet && testcalls > simulatedopcount)
823                         prt("skipping zero size write\n");
824                 log4(OP_SKIPPED, OP_MAPWRITE, offset, size, &t, tf);
825                 return;
826         }
827         cur_filesize = file_size;
828
829         log4(OP_MAPWRITE, offset, size, 0, &t, tf);
830
831         gendata(original_buf, good_buf, offset, size);
832         if (file_size < offset + size) {
833                 if (file_size < offset)
834                         memset(good_buf + file_size, '\0', offset - file_size);
835                 file_size = offset + size;
836                 if (lite) {
837                         warn("Lite file size bug in fsx!");
838                         report_failure(200);
839                 }
840         }
841
842         if (testcalls <= simulatedopcount)
843                 return;
844
845         output_line(tf, OP_MAPWRITE, offset, size, &t);
846
847         if (file_size > cur_filesize) {
848                 if (ftruncate(fd, file_size) == -1) {
849                         prterr("domapwrite: ftruncate");
850                         exit(201);
851                 }
852                 if (!quiet && (debug > 1 &&
853                                (monitorstart == -1 ||
854                                 (offset + size > monitorstart &&
855                                  (monitorend == -1 || offset <= monitorend))))) {
856                         gettimeofday(&t, NULL);
857                         prt("       %lu.%06lu truncate done\n", t.tv_sec, t.tv_usec);
858         }
859         }
860         pg_offset = offset & page_mask;
861         map_size  = pg_offset + size;
862
863         if ((p = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_FILE|MAP_SHARED,
864                       fd, (off_t)(offset - pg_offset))) == MAP_FAILED) {
865                 prterr("domapwrite: mmap");
866                 report_failure(202);
867         }
868         if (!quiet && (debug > 1 &&
869                         (monitorstart == -1 ||
870                          (offset + size > monitorstart &&
871                           (monitorend == -1 || offset <= monitorend))))) {
872                 gettimeofday(&t, NULL);
873                 prt("       %lu.%06lu mmap done\n", t.tv_sec, t.tv_usec);
874         }
875         memcpy(p + pg_offset, good_buf + offset, size);
876         if (!quiet && (debug > 1 &&
877                         (monitorstart == -1 ||
878                          (offset + size > monitorstart &&
879                           (monitorend == -1 || offset <= monitorend))))) {
880                 gettimeofday(&t, NULL);
881                 prt("       %lu.%06lu memcpy done\n", t.tv_sec, t.tv_usec);
882         }
883         if (msync(p, map_size, 0) != 0) {
884                 prterr("domapwrite: msync");
885                 report_failure(203);
886         }
887         if (!quiet && (debug > 1 &&
888                         (monitorstart == -1 ||
889                          (offset + size > monitorstart &&
890                           (monitorend == -1 || offset <= monitorend))))) {
891                 gettimeofday(&t, NULL);
892                 prt("       %lu.%06lu msync done\n", t.tv_sec, t.tv_usec);
893         }
894         if (munmap(p, map_size) != 0) {
895                 prterr("domapwrite: munmap");
896                 report_failure(204);
897         }
898         if (!quiet && (debug > 1 &&
899                         (monitorstart == -1 ||
900                          (offset + size > monitorstart &&
901                           (monitorend == -1 || offset <= monitorend))))) {
902                 gettimeofday(&t, NULL);
903                 prt("       %lu.%06lu munmap done\n", t.tv_sec, t.tv_usec);
904         }
905 }
906
907
908 void
909 dotruncate(unsigned size)
910 {
911         struct timeval t;
912         int oldsize = file_size;
913         struct test_file *tf = get_tf();
914         int fd = tf->fd;
915
916         size -= size % truncbdy;
917         gettimeofday(&t, NULL);
918         if (size > biggest) {
919                 biggest = size;
920                 if (!quiet && testcalls > simulatedopcount)
921                         prt("truncating to largest ever: 0x%x\n", size);
922         }
923
924         log4(OP_TRUNCATE, size, (unsigned)file_size, 0, &t, tf);
925
926         if (size > file_size)
927                 memset(good_buf + file_size, '\0', size - file_size);
928         file_size = size;
929
930         if (testcalls <= simulatedopcount)
931                 return;
932
933         output_line(tf, OP_TRUNCATE, oldsize, size, &t);
934
935         if (ftruncate(fd, (off_t)size) == -1) {
936                 prt("ftruncate1: %x\n", size);
937                 prterr("dotruncate: ftruncate");
938                 report_failure(160);
939         }
940         if (!quiet && debug > 1) {
941                 gettimeofday(&t, NULL);
942                 prt("       %lu.%06lu trunc done\n", t.tv_sec, t.tv_usec);
943         }
944 }
945
946
947 void
948 writefileimage()
949 {
950         ssize_t iret;
951         int fd = get_fd();
952
953         if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
954                 prterr("writefileimage: lseek");
955                 report_failure(171);
956         }
957         iret = write(fd, good_buf, file_size);
958         if ((off_t)iret != file_size) {
959                 if (iret == -1)
960                         prterr("writefileimage: write");
961                 else
962                         prt("short write: 0x%lx bytes instead of 0x%llx\n",
963                             (unsigned long)iret,
964                             (unsigned long long)file_size);
965                 report_failure(172);
966         }
967         if (lite ? 0 : ftruncate(fd, file_size) == -1) {
968                 prt("ftruncate2: %llx\n", (unsigned long long)file_size);
969                 prterr("writefileimage: ftruncate");
970                 report_failure(173);
971         }
972 }
973
974
975 void
976 docloseopen(void)
977 {
978         struct timeval t;
979         struct test_file *tf = get_tf();
980
981         if (testcalls <= simulatedopcount)
982                 return;
983
984         gettimeofday(&t, NULL);
985         log4(OP_CLOSEOPEN, file_size, (unsigned)file_size, 0, &t, tf);
986
987         if (debug)
988                 prt("%06lu %lu.%06lu close/open\n", testcalls, t.tv_sec,
989                     t.tv_usec);
990         if (close(tf->fd)) {
991                 prterr("docloseopen: close");
992                 report_failure(180);
993         }
994         if (!quiet && debug > 1) {
995                 gettimeofday(&t, NULL);
996                 prt("       %lu.%06lu close done\n", t.tv_sec, t.tv_usec);
997         }
998         tf->fd = open(tf->path, O_RDWR, 0);
999         if (tf->fd < 0) {
1000                 prterr("docloseopen: open");
1001                 report_failure(181);
1002         }
1003         if (!quiet && debug > 1) {
1004                 gettimeofday(&t, NULL);
1005                 prt("       %lu.%06lu open done\n", t.tv_sec, t.tv_usec);
1006         }
1007 }
1008
1009
1010 void
1011 test(void)
1012 {
1013         unsigned long   offset;
1014         unsigned long   size = maxoplen;
1015         unsigned long   rv = random();
1016         unsigned long   op = rv % (3 + !lite + mapped_writes);
1017
1018         /* turn off the map read if necessary */
1019
1020         if (op == 2 && !mapped_reads)
1021             op = 0;
1022
1023         if (simulatedopcount > 0 && testcalls == simulatedopcount)
1024                 writefileimage();
1025
1026         testcalls++;
1027
1028         if (debugstart > 0 && testcalls >= debugstart)
1029                 debug = 1;
1030
1031         if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
1032                 prt("%lu...\n", testcalls);
1033
1034         /*
1035          * READ:        op = 0
1036          * WRITE:       op = 1
1037          * MAPREAD:     op = 2
1038          * TRUNCATE:    op = 3
1039          * MAPWRITE:    op = 3 or 4
1040          */
1041         if (lite ? 0 : op == 3 && (style & 1) == 0) /* vanilla truncate? */
1042                 dotruncate(random() % maxfilelen);
1043         else {
1044                 if (randomoplen)
1045                         size = random() % (maxoplen+1);
1046                 if (lite ? 0 : op == 3)
1047                         dotruncate(size);
1048                 else {
1049                         offset = random();
1050                         if (op == 1 || op == (lite ? 3 : 4)) {
1051                                 offset %= maxfilelen;
1052                                 if (offset + size > maxfilelen)
1053                                         size = maxfilelen - offset;
1054                                 if (op != 1)
1055                                         domapwrite(offset, size);
1056                                 else
1057                                         dowrite(offset, size);
1058                         } else {
1059                                 if (file_size)
1060                                         offset %= file_size;
1061                                 else
1062                                         offset = 0;
1063                                 if (offset + size > file_size)
1064                                         size = file_size - offset;
1065                                 if (op != 0)
1066                                         domapread(offset, size);
1067                                 else
1068                                         doread(offset, size);
1069                         }
1070                 }
1071         }
1072         if (sizechecks && testcalls > simulatedopcount)
1073                 check_size();
1074         if (closeprob && (rv >> 3) < (1 << 28) / closeprob)
1075                 docloseopen();
1076 }
1077
1078
1079 void
1080 cleanup(sig)
1081         int     sig;
1082 {
1083         if (sig)
1084                 prt("signal %d\n", sig);
1085         prt("testcalls = %lu\n", testcalls);
1086         exit(sig);
1087 }
1088
1089
1090 void
1091 usage(void)
1092 {
1093         fprintf(stdout, "usage: %s",
1094                 "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m "
1095 "start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t "
1096 "truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] "
1097 "[ -I random|rotate ] fname [additional paths to fname..]\n"
1098 "       -b opnum: beginning operation number (default 1)\n"
1099 "       -c P: 1 in P chance of file close+open at each op (default infinity)\n"
1100 "       -d: debug output for all operations [-d -d = more debugging]\n"
1101 "       -l flen: the upper bound on file size (default 262144)\n"
1102 "       -m startop:endop: monitor (print debug output) specified byte rang"
1103 "(default 0:infinity)\n"
1104 "       -n: no verifications of file size\n"
1105 "       -o oplen: the upper bound on operation size (default 65536)\n"
1106 "       -p progressinterval: debug output at specified operation interval\n"
1107 "       -q: quieter operation\n"
1108 "       -r readbdy: 4096 would make reads page aligned (default 1)\n"
1109 "       -s style: 1 gives smaller truncates (default 0)\n"
1110 "       -t truncbdy: 4096 would make truncates page aligned (default 1)\n"
1111 "       -w writebdy: 4096 would make writes page aligned (default 1)\n"
1112 "       -D startingop: debug output starting at specified operation\n"
1113 "       -L: fsxLite - no file creations & no file size changes\n"
1114 "       -N numops: total # operations to do (default infinity)\n"
1115 "       -O: use oplen (see -o flag) for every op (default random)\n"
1116 "       -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n"
1117 "       -S seed: for random # generator (default 1) 0 gets timestamp\n"
1118 "       -W: mapped write operations DISabled\n"
1119 "        -R: read() system calls only (mapped reads disabled)\n"
1120 "       -I: When multiple paths to the file are given each operation uses"
1121 "           a different path.  Iterate through them in order with 'rotate'"
1122 "           or chose then at 'random'.  (defaults to random)\n"
1123 "       fname: this filename is REQUIRED (no default)\n");
1124         exit(90);
1125 }
1126
1127
1128 int
1129 getnum(char *s, char **e)
1130 {
1131         int ret = -1;
1132
1133         *e = (char *) 0;
1134         ret = strtol(s, e, 0);
1135         if (*e)
1136                 switch (**e) {
1137                 case 'b':
1138                 case 'B':
1139                         ret *= 512;
1140                         *e = *e + 1;
1141                         break;
1142                 case 'k':
1143                 case 'K':
1144                         ret *= 1024;
1145                         *e = *e + 1;
1146                         break;
1147                 case 'm':
1148                 case 'M':
1149                         ret *= 1024*1024;
1150                         *e = *e + 1;
1151                         break;
1152                 case 'w':
1153                 case 'W':
1154                         ret *= 4;
1155                         *e = *e + 1;
1156                         break;
1157                 }
1158         return (ret);
1159 }
1160
1161 int
1162 main(int argc, char **argv)
1163 {
1164         int     i, style, ch;
1165         char    *endp;
1166         int  dirpath = 0;
1167
1168         goodfile[0] = 0;
1169         logfile[0] = 0;
1170
1171         page_size = getpagesize();
1172         page_mask = page_size - 1;
1173
1174         setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
1175
1176         while ((ch = getopt(argc, argv,
1177                                 "b:c:dl:m:no:p:qr:s:t:w:D:I:LN:OP:RS:W"))
1178                != EOF)
1179                 switch (ch) {
1180                 case 'b':
1181                         simulatedopcount = getnum(optarg, &endp);
1182                         if (!quiet)
1183                                 fprintf(stdout, "Will begin at operation"
1184                                         "%ld\n",
1185                                         simulatedopcount);
1186                         if (simulatedopcount == 0)
1187                                 usage();
1188                         simulatedopcount -= 1;
1189                         break;
1190                 case 'c':
1191                         closeprob = getnum(optarg, &endp);
1192                         if (!quiet)
1193                                 fprintf(stdout,
1194                                         "Chance of close/open is 1 in %d\n",
1195                                         closeprob);
1196                         if (closeprob <= 0)
1197                                 usage();
1198                         break;
1199                 case 'd':
1200                         debug++;
1201                         break;
1202                 case 'l':
1203                         maxfilelen = getnum(optarg, &endp);
1204                         if (maxfilelen <= 0)
1205                                 usage();
1206                         break;
1207                 case 'm':
1208                         monitorstart = getnum(optarg, &endp);
1209                         if (monitorstart < 0)
1210                                 usage();
1211                         if (!endp || *endp++ != ':')
1212                                 usage();
1213                         monitorend = getnum(endp, &endp);
1214                         if (monitorend < 0)
1215                                 usage();
1216                         if (monitorend == 0)
1217                                 monitorend = -1; /* aka infinity */
1218                         debug = 1;
1219                 case 'n':
1220                         sizechecks = 0;
1221                         break;
1222                 case 'o':
1223                         maxoplen = getnum(optarg, &endp);
1224                         if (maxoplen <= 0)
1225                                 usage();
1226                         break;
1227                 case 'p':
1228                         progressinterval = getnum(optarg, &endp);
1229                         if (progressinterval < 0)
1230                                 usage();
1231                         break;
1232                 case 'q':
1233                         quiet = 1;
1234                         break;
1235                 case 'r':
1236                         readbdy = getnum(optarg, &endp);
1237                         if (readbdy <= 0)
1238                                 usage();
1239                         break;
1240                 case 's':
1241                         style = getnum(optarg, &endp);
1242                         if (style < 0 || style > 1)
1243                                 usage();
1244                         break;
1245                 case 't':
1246                         truncbdy = getnum(optarg, &endp);
1247                         if (truncbdy <= 0)
1248                                 usage();
1249                         break;
1250                 case 'w':
1251                         writebdy = getnum(optarg, &endp);
1252                         if (writebdy <= 0)
1253                                 usage();
1254                         break;
1255                 case 'D':
1256                         debugstart = getnum(optarg, &endp);
1257                         if (debugstart < 1)
1258                                 usage();
1259                         break;
1260                 case 'I':
1261                         assign_fd_policy(optarg);
1262                         break;
1263                 case 'L':
1264                         lite = 1;
1265                         break;
1266                 case 'N':
1267                         numops = getnum(optarg, &endp);
1268                         if (numops < 0)
1269                                 usage();
1270                         break;
1271                 case 'O':
1272                         randomoplen = 0;
1273                         break;
1274                 case 'P':
1275                         strncpy(goodfile, optarg, sizeof(goodfile));
1276                         strcat(goodfile, "/");
1277                         strncpy(logfile, optarg, sizeof(logfile));
1278                         strcat(logfile, "/");
1279                         dirpath = 1;
1280                         break;
1281                 case 'R':
1282                         mapped_reads = 0;
1283                         break;
1284                 case 'S':
1285                         seed = getnum(optarg, &endp);
1286                         if (seed == 0)
1287                                 seed = time(0) % 10000;
1288                         if (!quiet)
1289                                 fprintf(stdout, "Seed set to %d\n", seed);
1290                         if (seed < 0)
1291                                 usage();
1292                         break;
1293                 case 'W':
1294                         mapped_writes = 0;
1295                         if (!quiet)
1296                                 fprintf(stdout, "mapped writes DISABLED\n");
1297                         break;
1298
1299                 default:
1300                         usage();
1301                         /* NOTREACHED */
1302                 }
1303         argc -= optind;
1304         argv += optind;
1305         if (argc < 1)
1306                 usage();
1307         fname = argv[0];
1308
1309         signal(SIGHUP,  cleanup);
1310         signal(SIGINT,  cleanup);
1311         signal(SIGPIPE, cleanup);
1312         signal(SIGALRM, cleanup);
1313         signal(SIGTERM, cleanup);
1314         signal(SIGXCPU, cleanup);
1315         signal(SIGXFSZ, cleanup);
1316         signal(SIGVTALRM,       cleanup);
1317         signal(SIGUSR1, cleanup);
1318         signal(SIGUSR2, cleanup);
1319
1320         initstate(seed, state, 256);
1321         setstate(state);
1322
1323         open_test_files(argv, argc);
1324
1325         strncat(goodfile, dirpath ? my_basename(fname) : fname, 256);
1326         strcat (goodfile, ".fsxgood");
1327         fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
1328         if (fsxgoodfd < 0) {
1329                 prterr(goodfile);
1330                 exit(92);
1331         }
1332         strncat(logfile, dirpath ? my_basename(fname) : fname, 256);
1333         strcat (logfile, ".fsxlog");
1334         fsxlogf = fopen(logfile, "w");
1335         if (fsxlogf == NULL) {
1336                 prterr(logfile);
1337                 exit(93);
1338         }
1339         if (lite) {
1340                 off_t ret;
1341                 int fd = get_fd();
1342                 file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END);
1343                 if (file_size == (off_t)-1) {
1344                         prterr(fname);
1345                         warn("main: lseek eof");
1346                         exit(94);
1347                 }
1348                 ret = lseek(fd, (off_t)0, SEEK_SET);
1349                 if (ret == (off_t)-1) {
1350                         prterr(fname);
1351                         warn("main: lseek 0");
1352                         exit(95);
1353                 }
1354         }
1355         original_buf = (char *) malloc(maxfilelen);
1356         for (i = 0; i < maxfilelen; i++)
1357                 original_buf[i] = random() % 256;
1358         good_buf = (char *) malloc(maxfilelen);
1359         memset(good_buf, '\0', maxfilelen);
1360         temp_buf = (char *) malloc(maxoplen);
1361         memset(temp_buf, '\0', maxoplen);
1362         if (lite) {     /* zero entire existing file */
1363                 ssize_t written;
1364                 int fd = get_fd();
1365
1366                 written = write(fd, good_buf, (size_t)maxfilelen);
1367                 if (written != maxfilelen) {
1368                         if (written == -1) {
1369                                 prterr(fname);
1370                                 warn("main: error on write");
1371                         } else
1372                                 warn("main: short write, 0x%x bytes instead"
1373                                         "of 0x%x\n",
1374                                      (unsigned)written, maxfilelen);
1375                         exit(98);
1376                 }
1377         } else
1378                 check_trunc_hack();
1379
1380         while (numops == -1 || numops--)
1381                 test();
1382
1383         close_test_files();
1384         prt("All operations completed A-OK!\n");
1385
1386         exit(0);
1387         return 0;
1388 }