2 * Copyright (C) 1991, NeXT Computer, Inc. All Rights Reserverd.
5 * Author: Avadis Tevanian, Jr.
7 * File system exerciser.
9 * Rewritten 8/98 by Conrad Minshall.
11 * Small changes to work under Linux -- davej.
13 * Checks for mmap last-page zero fill.
36 #include <linux/falloc.h>
43 #define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */
46 * A log entry is an operation and a bunch of arguments.
56 struct log_entry oplog[LOGSIZE]; /* the log */
57 int logptr = 0; /* current position in log */
58 int logcount = 0; /* total ops */
67 #define OP_CLOSEOPEN 4
71 #define OP_FALLOCATE 8
72 #define OP_PUNCH_HOLE 9
75 #define PAGE_SIZE getpagesize()
77 #define PAGE_MASK (PAGE_SIZE - 1)
79 char *original_buf; /* a pointer to the original data */
80 char *good_buf; /* a pointer to the correct data */
81 char *temp_buf; /* a pointer to the current data */
82 char *fname; /* name of our test file */
83 int fd; /* fd for our test file */
88 unsigned long testcalls = 0; /* calls to function "test" */
90 unsigned long simulatedopcount = 0; /* -b flag */
91 int closeprob = 0; /* -c flag */
92 int debug = 0; /* -d flag */
93 unsigned long debugstart = 0; /* -D flag */
94 int flush = 0; /* -f flag */
95 int do_fsync = 0; /* -y flag */
96 unsigned long maxfilelen = 256 * 1024; /* -l flag */
97 int sizechecks = 1; /* -n flag disables them */
98 int maxoplen = 64 * 1024; /* -o flag */
99 int quiet = 0; /* -q flag */
100 unsigned long progressinterval = 0; /* -p flag */
101 int readbdy = 1; /* -r flag */
102 int style = 0; /* -s flag */
103 int prealloc = 0; /* -x flag */
104 int truncbdy = 1; /* -t flag */
105 int writebdy = 1; /* -w flag */
106 long monitorstart = -1; /* -m flag */
107 long monitorend = -1; /* -m flag */
108 int lite = 0; /* -L flag */
109 long numops = -1; /* -N flag */
110 int randomoplen = 1; /* -O flag disables it */
111 int seed = 1; /* -S flag */
112 int mapped_writes = 1; /* -W flag disables */
113 int fallocate_calls = 1; /* -F flag disables */
114 int punch_hole_calls = 1; /* -H flag disables */
115 int mapped_reads = 1; /* -R flag disables it */
117 int o_direct; /* -Z */
124 int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset);
127 #define fsxread(a,b,c,d) aio_rw(READ, a,b,c,d)
128 #define fsxwrite(a,b,c,d) aio_rw(WRITE, a,b,c,d)
130 #define fsxread(a,b,c,d) read(a,b,c)
131 #define fsxwrite(a,b,c,d) write(a,b,c)
134 FILE * fsxlogf = NULL;
138 static void *round_up(void *ptr, unsigned long align, unsigned long offset)
140 unsigned long ret = (unsigned long)ptr;
142 ret = ((ret + align - 1) & ~(align - 1));
148 vwarnc(int code, const char *fmt, va_list ap) {
149 fprintf(stderr, "fsx: ");
151 vfprintf(stderr, fmt, ap);
152 fprintf(stderr, ": ");
154 fprintf(stderr, "%s\n", strerror(code));
158 warn(const char * fmt, ...) {
161 vwarnc(errno, fmt, ap);
165 #define BUF_SIZE 1024
171 char buffer[BUF_SIZE];
174 vsnprintf(buffer, BUF_SIZE, fmt, args);
176 fprintf(stdout, buffer);
178 fprintf(fsxlogf, buffer);
184 prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
189 log4(int operation, int arg0, int arg1, int arg2)
191 struct log_entry *le;
194 le->operation = operation;
196 le->operation = ~ le->operation;
202 if (logptr >= LOGSIZE)
211 struct log_entry *lp;
212 char *falloc_type[3] = {"PAST_EOF", "EXTENDING", "INTERIOR"};
214 prt("LOG DUMP (%d total operations):\n", logcount);
215 if (logcount < LOGSIZE) {
222 for ( ; count > 0; count--) {
225 opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE;
226 prt("%d(%d mod 256): ", opnum, opnum%256);
228 if ((closeopen = lp->operation < 0))
229 lp->operation = ~ lp->operation;
231 switch (lp->operation) {
233 prt("MAPREAD\t0x%x thru 0x%x\t(0x%x bytes)",
234 lp->args[0], lp->args[0] + lp->args[1] - 1,
236 if (badoff >= lp->args[0] && badoff <
237 lp->args[0] + lp->args[1])
241 prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)",
242 lp->args[0], lp->args[0] + lp->args[1] - 1,
244 if (badoff >= lp->args[0] && badoff <
245 lp->args[0] + lp->args[1])
249 prt("READ\t0x%x thru 0x%x\t(0x%x bytes)",
250 lp->args[0], lp->args[0] + lp->args[1] - 1,
252 if (badoff >= lp->args[0] &&
253 badoff < lp->args[0] + lp->args[1])
257 prt("WRITE\t0x%x thru 0x%x\t(0x%x bytes)",
258 lp->args[0], lp->args[0] + lp->args[1] - 1,
260 if (lp->args[0] > lp->args[2])
262 else if (lp->args[0] + lp->args[1] > lp->args[2])
264 if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
265 badoff < lp->args[0] + lp->args[1])
269 down = lp->args[0] < lp->args[1];
270 prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
271 down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
272 if (badoff >= lp->args[!down] &&
273 badoff < lp->args[!!down])
277 /* 0: offset 1: length 2: where alloced */
278 prt("FALLOCATE %s\tfrom 0x%x to 0x%x",
279 falloc_type[lp->args[2]], lp->args[0], lp->args[0] + lp->args[1]);
280 if (badoff >= lp->args[0] &&
281 badoff < lp->args[0] + lp->args[1])
285 prt("PUNCH HOLE\t0x%x thru 0x%x\t(0x%x bytes)",
286 lp->args[0], lp->args[0] + lp->args[1] - 1,
288 if (badoff >= lp->args[0] && badoff <
289 lp->args[0] + lp->args[1])
293 prt("SKIPPED (no operation)");
296 prt("BOGUS LOG ENTRY (operation code = %d)!",
300 prt("\n\t\tCLOSE/OPEN");
310 save_buffer(char *buffer, off_t bufferlength, int fd)
313 ssize_t byteswritten;
315 if (fd <= 0 || bufferlength == 0)
318 if (bufferlength > SSIZE_MAX) {
319 prt("fsx flaw: overflow in save_buffer\n");
323 off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END);
324 if (size_by_seek == (off_t)-1)
325 prterr("save_buffer: lseek eof");
326 else if (bufferlength > size_by_seek) {
327 warn("save_buffer: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek,
328 (unsigned long long)bufferlength);
329 bufferlength = size_by_seek;
333 ret = lseek(fd, (off_t)0, SEEK_SET);
334 if (ret == (off_t)-1)
335 prterr("save_buffer: lseek 0");
337 byteswritten = write(fd, buffer, (size_t)bufferlength);
338 if (byteswritten != bufferlength) {
339 if (byteswritten == -1)
340 prterr("save_buffer write");
342 warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n",
343 (unsigned)byteswritten,
344 (unsigned long long)bufferlength);
350 report_failure(int status)
356 save_buffer(good_buf, file_size, fsxgoodfd);
357 prt("Correct content saved for comparison\n");
358 prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n",
367 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
368 *(((unsigned char *)(cp)) + 1)))
371 check_buffers(unsigned offset, unsigned size)
379 if (memcmp(good_buf + offset, temp_buf, size) != 0) {
380 prt("READ BAD DATA: offset = 0x%x, size = 0x%x, fname = %s\n",
381 offset, size, fname);
382 prt("OFFSET\tGOOD\tBAD\tRANGE\n");
384 c = good_buf[offset];
388 bad = short_at(&temp_buf[i]);
389 prt("0x%5x\t0x%04x\t0x%04x", offset,
390 short_at(&good_buf[offset]), bad);
391 op = temp_buf[offset & 1 ? i+1 : i];
394 prt("operation# (mod 256) for "
395 "the bad data may be %u\n",
396 ((unsigned)op & 0xff));
398 prt("operation# (mod 256) for "
399 "the bad data unknown, check"
400 " HOLE and EXTEND ops\n");
420 if (fstat(fd, &statbuf)) {
421 prterr("check_size: fstat");
422 statbuf.st_size = -1;
424 size_by_seek = lseek(fd, (off_t)0, SEEK_END);
425 if (file_size != statbuf.st_size || file_size != size_by_seek) {
426 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
427 (unsigned long long)file_size,
428 (unsigned long long)statbuf.st_size,
429 (unsigned long long)size_by_seek);
436 check_trunc_hack(void)
440 ftruncate(fd, (off_t)0);
441 ftruncate(fd, (off_t)100000);
443 if (statbuf.st_size != (off_t)100000) {
444 prt("no extend on truncate! not posix!\n");
451 doflush(unsigned offset, unsigned size)
457 if (o_direct == O_DIRECT)
460 pg_offset = offset & mmap_mask;
461 map_size = pg_offset + size;
463 if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
464 MAP_FILE | MAP_SHARED, fd,
465 (off_t)(offset - pg_offset))) == (char *)-1) {
466 prterr("doflush: mmap");
469 if (msync(p, map_size, MS_INVALIDATE) != 0) {
470 prterr("doflush: msync");
473 if (munmap(p, map_size) != 0) {
474 prterr("doflush: munmap");
480 doread(unsigned offset, unsigned size)
485 offset -= offset % readbdy;
487 size -= size % readbdy;
489 if (!quiet && testcalls > simulatedopcount && !o_direct)
490 prt("skipping zero size read\n");
491 log4(OP_SKIPPED, OP_READ, offset, size);
494 if (size + offset > file_size) {
495 if (!quiet && testcalls > simulatedopcount)
496 prt("skipping seek/read past end of file\n");
497 log4(OP_SKIPPED, OP_READ, offset, size);
501 log4(OP_READ, offset, size, 0);
503 if (testcalls <= simulatedopcount)
507 ((progressinterval && testcalls % progressinterval == 0) ||
509 (monitorstart == -1 ||
510 (offset + size > monitorstart &&
511 (monitorend == -1 || offset <= monitorend))))))
512 prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
513 offset, offset + size - 1, size);
514 ret = lseek(fd, (off_t)offset, SEEK_SET);
515 if (ret == (off_t)-1) {
516 prterr("doread: lseek");
519 iret = fsxread(fd, temp_buf, size, offset);
522 prterr("doread: read");
524 prt("short read: 0x%x bytes instead of 0x%x\n",
528 check_buffers(offset, size);
533 check_eofpage(char *s, unsigned offset, char *p, int size)
535 unsigned long last_page, should_be_zero;
537 if (offset + size <= (file_size & ~page_mask))
540 * we landed in the last page of the file
541 * test to make sure the VM system provided 0's
542 * beyond the true end of the file mapping
543 * (as required by mmap def in 1996 posix 1003.1)
545 last_page = ((unsigned long)p + (offset & page_mask) + size) & ~page_mask;
547 for (should_be_zero = last_page + (file_size & page_mask);
548 should_be_zero < last_page + page_size;
550 if (*(char *)should_be_zero) {
551 prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n",
552 s, file_size - 1, should_be_zero & page_mask,
553 short_at(should_be_zero));
560 domapread(unsigned offset, unsigned size)
566 offset -= offset % readbdy;
568 if (!quiet && testcalls > simulatedopcount)
569 prt("skipping zero size read\n");
570 log4(OP_SKIPPED, OP_MAPREAD, offset, size);
573 if (size + offset > file_size) {
574 if (!quiet && testcalls > simulatedopcount)
575 prt("skipping seek/read past end of file\n");
576 log4(OP_SKIPPED, OP_MAPREAD, offset, size);
580 log4(OP_MAPREAD, offset, size, 0);
582 if (testcalls <= simulatedopcount)
586 ((progressinterval && testcalls % progressinterval == 0) ||
588 (monitorstart == -1 ||
589 (offset + size > monitorstart &&
590 (monitorend == -1 || offset <= monitorend))))))
591 prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
592 offset, offset + size - 1, size);
594 pg_offset = offset & PAGE_MASK;
595 map_size = pg_offset + size;
597 if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_SHARED, fd,
598 (off_t)(offset - pg_offset))) == (char *)-1) {
599 prterr("domapread: mmap");
602 memcpy(temp_buf, p + pg_offset, size);
604 check_eofpage("Read", offset, p, size);
606 if (munmap(p, map_size) != 0) {
607 prterr("domapread: munmap");
611 check_buffers(offset, size);
616 gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
619 good_buf[offset] = testcalls % 256;
621 good_buf[offset] += original_buf[offset];
628 dowrite(unsigned offset, unsigned size)
633 offset -= offset % writebdy;
635 size -= size % writebdy;
637 if (!quiet && testcalls > simulatedopcount && !o_direct)
638 prt("skipping zero size write\n");
639 log4(OP_SKIPPED, OP_WRITE, offset, size);
643 log4(OP_WRITE, offset, size, file_size);
645 gendata(original_buf, good_buf, offset, size);
646 if (file_size < offset + size) {
647 if (file_size < offset)
648 memset(good_buf + file_size, '\0', offset - file_size);
649 file_size = offset + size;
651 warn("Lite file size bug in fsx!");
656 if (testcalls <= simulatedopcount)
660 ((progressinterval && testcalls % progressinterval == 0) ||
662 (monitorstart == -1 ||
663 (offset + size > monitorstart &&
664 (monitorend == -1 || offset <= monitorend))))))
665 prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
666 offset, offset + size - 1, size);
667 ret = lseek(fd, (off_t)offset, SEEK_SET);
668 if (ret == (off_t)-1) {
669 prterr("dowrite: lseek");
672 iret = fsxwrite(fd, good_buf + offset, size, offset);
675 prterr("dowrite: write");
677 prt("short write: 0x%x bytes instead of 0x%x\n",
683 prt("fsync() failed: %s\n", strerror(errno));
688 doflush(offset, size);
694 domapwrite(unsigned offset, unsigned size)
701 offset -= offset % writebdy;
703 if (!quiet && testcalls > simulatedopcount)
704 prt("skipping zero size write\n");
705 log4(OP_SKIPPED, OP_MAPWRITE, offset, size);
708 cur_filesize = file_size;
710 log4(OP_MAPWRITE, offset, size, 0);
712 gendata(original_buf, good_buf, offset, size);
713 if (file_size < offset + size) {
714 if (file_size < offset)
715 memset(good_buf + file_size, '\0', offset - file_size);
716 file_size = offset + size;
718 warn("Lite file size bug in fsx!");
723 if (testcalls <= simulatedopcount)
727 ((progressinterval && testcalls % progressinterval == 0) ||
729 (monitorstart == -1 ||
730 (offset + size > monitorstart &&
731 (monitorend == -1 || offset <= monitorend))))))
732 prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
733 offset, offset + size - 1, size);
735 if (file_size > cur_filesize) {
736 if (ftruncate(fd, file_size) == -1) {
737 prterr("domapwrite: ftruncate");
741 pg_offset = offset & PAGE_MASK;
742 map_size = pg_offset + size;
744 if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
745 MAP_FILE | MAP_SHARED, fd,
746 (off_t)(offset - pg_offset))) == (char *)-1) {
747 prterr("domapwrite: mmap");
750 memcpy(p + pg_offset, good_buf + offset, size);
751 if (msync(p, map_size, 0) != 0) {
752 prterr("domapwrite: msync");
756 check_eofpage("Write", offset, p, size);
758 if (munmap(p, map_size) != 0) {
759 prterr("domapwrite: munmap");
766 dotruncate(unsigned size)
768 int oldsize = file_size;
770 size -= size % truncbdy;
771 if (size > biggest) {
773 if (!quiet && testcalls > simulatedopcount)
774 prt("truncating to largest ever: 0x%x\n", size);
777 log4(OP_TRUNCATE, size, (unsigned)file_size, 0);
779 if (size > file_size)
780 memset(good_buf + file_size, '\0', size - file_size);
783 if (testcalls <= simulatedopcount)
786 if ((progressinterval && testcalls % progressinterval == 0) ||
787 (debug && (monitorstart == -1 || monitorend == -1 ||
788 size <= monitorend)))
789 prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size);
790 if (ftruncate(fd, (off_t)size) == -1) {
791 prt("ftruncate1: %x\n", size);
792 prterr("dotruncate: ftruncate");
797 #ifdef FALLOC_FL_PUNCH_HOLE
799 do_punch_hole(unsigned offset, unsigned length)
804 int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
807 if (!quiet && testcalls > simulatedopcount)
808 prt("skipping zero length punch hole\n");
809 log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length);
813 if (file_size <= (loff_t)offset) {
814 if (!quiet && testcalls > simulatedopcount)
815 prt("skipping hole punch off the end of the file\n");
816 log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length);
820 end_offset = offset + length;
822 log4(OP_PUNCH_HOLE, offset, length, 0);
824 if (testcalls <= simulatedopcount)
827 if ((progressinterval && testcalls % progressinterval == 0) ||
828 (debug && (monitorstart == -1 || monitorend == -1 ||
829 end_offset <= monitorend))) {
830 prt("%lu punch\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
831 offset, offset+length, length);
833 if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
834 prt("%punch hole: %x to %x\n", offset, length);
835 prterr("do_punch_hole: fallocate");
840 max_offset = offset < file_size ? offset : file_size;
841 max_len = max_offset + length <= file_size ? length :
842 file_size - max_offset;
843 memset(good_buf + max_offset, '\0', max_len);
848 do_punch_hole(unsigned offset, unsigned length)
855 /* fallocate is basically a no-op unless extending, then a lot like a truncate */
857 do_preallocate(unsigned offset, unsigned length)
863 if (!quiet && testcalls > simulatedopcount)
864 prt("skipping zero length fallocate\n");
865 log4(OP_SKIPPED, OP_FALLOCATE, offset, length);
869 keep_size = random() % 2;
871 end_offset = keep_size ? 0 : offset + length;
873 if (end_offset > biggest) {
874 biggest = end_offset;
875 if (!quiet && testcalls > simulatedopcount)
876 prt("fallocating to largest ever: 0x%x\n", end_offset);
881 * 1: allocate past EOF
882 * 2: extending prealloc
883 * 3: interior prealloc
885 log4(OP_FALLOCATE, offset, length, (end_offset > file_size) ? (keep_size ? 1 : 2) : 3);
887 if (end_offset > file_size) {
888 memset(good_buf + file_size, '\0', end_offset - file_size);
889 file_size = end_offset;
892 if (testcalls <= simulatedopcount)
895 if ((progressinterval && testcalls % progressinterval == 0) ||
896 (debug && (monitorstart == -1 || monitorend == -1 ||
897 end_offset <= monitorend)))
898 prt("%lu falloc\tfrom 0x%x to 0x%x\n", testcalls, offset, length);
899 if (fallocate(fd, keep_size ? FALLOC_FL_KEEP_SIZE : 0, (loff_t)offset, (loff_t)length) == -1) {
900 prt("fallocate: %x to %x\n", offset, length);
901 prterr("do_preallocate: fallocate");
907 do_preallocate(unsigned offset, unsigned length)
918 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
919 prterr("writefileimage: lseek");
922 iret = write(fd, good_buf, file_size);
923 if ((off_t)iret != file_size) {
925 prterr("writefileimage: write");
927 prt("short write: 0x%x bytes instead of 0x%llx\n",
928 iret, (unsigned long long)file_size);
931 if (lite ? 0 : ftruncate(fd, file_size) == -1) {
932 prt("ftruncate2: %llx\n", (unsigned long long)file_size);
933 prterr("writefileimage: ftruncate");
942 if (testcalls <= simulatedopcount)
946 prt("%lu close/open\n", testcalls);
948 prterr("docloseopen: close");
951 fd = open(fname, O_RDWR|o_direct, 0);
953 prterr("docloseopen: open");
962 unsigned long offset;
963 unsigned long size = maxoplen;
964 unsigned long rv = random();
965 unsigned long op = rv % (3 + !lite + mapped_writes + fallocate_calls + punch_hole_calls);
966 /* turn off the map read if necessary */
968 if (op == 2 && !mapped_reads)
971 if (simulatedopcount > 0 && testcalls == simulatedopcount)
977 closeopen = (rv >> 3) < (1 << 28) / closeprob;
979 if (debugstart > 0 && testcalls >= debugstart)
982 if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
983 prt("%lu...\n", testcalls);
992 * FALLOCATE: op = - 5
993 * PUNCH HOLE: op = - 6
995 if (lite ? 0 : op == 3 && (style & 1) == 0) /* vanilla truncate? */
996 dotruncate(random() % maxfilelen);
999 size = random() % (maxoplen+1);
1001 if (lite ? 0 : op == 3) {
1008 offset %= maxfilelen;
1009 if (offset + size > maxfilelen)
1010 size = maxfilelen - offset;
1011 do_preallocate(offset, size);
1012 } else if (op == 6) {
1013 offset %= maxfilelen;
1014 if (offset + size > maxfilelen)
1015 size = maxfilelen - offset;
1016 do_punch_hole(offset, size);
1017 } else if (op == 1 || op == (lite ? 3 : 4)) {
1018 /* write / mapwrite */
1019 offset %= maxfilelen;
1020 if (offset + size > maxfilelen)
1021 size = maxfilelen - offset;
1023 domapwrite(offset, size);
1025 dowrite(offset, size);
1027 /* read / mapread */
1029 offset %= file_size;
1032 if (offset + size > file_size)
1033 size = file_size - offset;
1035 domapread(offset, size);
1037 doread(offset, size);
1041 if (sizechecks && testcalls > simulatedopcount)
1053 prt("signal %d\n", sig);
1054 prt("testcalls = %lu\n", testcalls);
1062 fprintf(stdout, "usage: %s",
1063 "fsx [-dnqxAFLOWZ] [-b opnum] [-c Prob] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\
1064 -b opnum: beginning operation number (default 1)\n\
1065 -c P: 1 in P chance of file close+open at each op (default infinity)\n\
1066 -d: debug output for all operations\n\
1067 -f flush and invalidate cache after I/O\n\
1068 -l flen: the upper bound on file size (default 262144)\n\
1069 -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\
1070 -n: no verifications of file size\n\
1071 -o oplen: the upper bound on operation size (default 65536)\n\
1072 -p progressinterval: debug output at specified operation interval\n\
1073 -q: quieter operation\n\
1074 -r readbdy: 4096 would make reads page aligned (default 1)\n\
1075 -s style: 1 gives smaller truncates (default 0)\n\
1076 -t truncbdy: 4096 would make truncates page aligned (default 1)\n\
1077 -w writebdy: 4096 would make writes page aligned (default 1)\n\
1078 -x: preallocate file space before starting, XFS only (default 0)\n\
1079 -y synchronize changes to a file\n"
1082 " -A: Use the AIO system calls\n"
1084 " -D startingop: debug output starting at specified operation\n"
1086 " -F: Do not use fallocate (preallocation) calls\n"
1088 #ifdef FALLOC_FL_PUNCH_HOLE
1089 " -H: Do not use punch hole calls\n"
1091 " -L: fsxLite - no file creations & no file size changes\n\
1092 -N numops: total # operations to do (default infinity)\n\
1093 -O: use oplen (see -o flag) for every op (default random)\n\
1094 -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n\
1095 -S seed: for random # generator (default 1) 0 gets timestamp\n\
1096 -W: mapped write operations DISabled\n\
1097 -R: read() system calls only (mapped reads disabled)\n\
1098 -Z: O_DIRECT (use -R, -W, -r and -w too)\n\
1099 fname: this filename is REQUIRED (no default)\n");
1105 getnum(char *s, char **e)
1110 ret = strtol(s, e, 0);
1140 io_context_t io_ctx;
1146 ret = io_queue_init(QSZ, &io_ctx);
1148 fprintf(stderr, "aio_setup: io_queue_init failed: %s\n",
1156 __aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
1158 struct io_event event;
1159 static struct timespec ts;
1160 struct iocb *iocbs[] = { &iocb };
1165 io_prep_pread(&iocb, fd, buf, len, offset);
1167 io_prep_pwrite(&iocb, fd, buf, len, offset);
1172 ret = io_submit(io_ctx, 1, iocbs);
1174 fprintf(stderr, "errcode=%d\n", ret);
1175 fprintf(stderr, "aio_rw: io_submit failed: %s\n",
1180 ret = io_getevents(io_ctx, 1, 1, &event, &ts);
1183 fprintf(stderr, "aio_rw: no events available\n");
1185 fprintf(stderr, "errcode=%d\n", -ret);
1186 fprintf(stderr, "aio_rw: io_getevents failed: %s\n",
1191 if (len != event.res) {
1193 * The b0rked libaio defines event.res as unsigned.
1194 * However the kernel strucuture has it signed,
1195 * and it's used to pass negated error value.
1196 * Till the library is fixed use the temp var.
1198 res = (long)event.res;
1200 fprintf(stderr, "bad io length: %lu instead of %u\n",
1203 fprintf(stderr, "errcode=%ld\n", -res);
1204 fprintf(stderr, "aio_rw: async io failed: %s\n",
1215 * The caller expects error return in traditional libc
1216 * convention, i.e. -1 and the errno set to error.
1222 int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
1227 ret = __aio_rw(rw, fd, buf, len, offset);
1230 ret = read(fd, buf, len);
1232 ret = write(fd, buf, len);
1243 if (!lite && fallocate_calls) {
1244 if (fallocate(fd, 0, 0, 1) && errno == EOPNOTSUPP) {
1246 prt("fsx: main: filesystem does not support fallocate, disabling\n");
1247 fallocate_calls = 0;
1252 #else /* ! FALLOCATE */
1253 fallocate_calls = 0;
1261 #ifdef FALLOC_FL_PUNCH_HOLE
1262 if (!lite && punch_hole_calls) {
1263 if (fallocate(fd, 0, 0,
1264 FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE) &&
1265 errno == EOPNOTSUPP) {
1267 warn("main: filesystem does not support fallocate punch hole, disabling");
1268 punch_hole_calls = 0;
1271 #else /* ! PUNCH HOLE */
1272 punch_hole_calls = 0;
1277 main(int argc, char **argv)
1281 char goodfile[1024];
1287 page_size = getpagesize();
1288 page_mask = page_size - 1;
1289 mmap_mask = page_mask;
1292 setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
1294 while ((ch = getopt(argc, argv, "b:c:dfl:m:no:p:qr:s:t:w:xyAD:FHLN:OP:RS:WZ"))
1298 simulatedopcount = getnum(optarg, &endp);
1300 fprintf(stdout, "Will begin at operation %ld\n",
1302 if (simulatedopcount == 0)
1304 simulatedopcount -= 1;
1307 closeprob = getnum(optarg, &endp);
1310 "Chance of close/open is 1 in %d\n",
1322 maxfilelen = getnum(optarg, &endp);
1323 if (maxfilelen <= 0)
1327 monitorstart = getnum(optarg, &endp);
1328 if (monitorstart < 0)
1330 if (!endp || *endp++ != ':')
1332 monitorend = getnum(endp, &endp);
1335 if (monitorend == 0)
1336 monitorend = -1; /* aka infinity */
1342 maxoplen = getnum(optarg, &endp);
1347 progressinterval = getnum(optarg, &endp);
1348 if (progressinterval == 0)
1355 readbdy = getnum(optarg, &endp);
1360 style = getnum(optarg, &endp);
1361 if (style < 0 || style > 1)
1365 truncbdy = getnum(optarg, &endp);
1370 writebdy = getnum(optarg, &endp);
1384 debugstart = getnum(optarg, &endp);
1389 fallocate_calls = 0;
1392 punch_hole_calls = 0;
1398 numops = getnum(optarg, &endp);
1406 strncpy(goodfile, optarg, sizeof(goodfile));
1407 strcat(goodfile, "/");
1408 strncpy(logfile, optarg, sizeof(logfile));
1409 strcat(logfile, "/");
1415 seed = getnum(optarg, &endp);
1417 seed = time(0) % 10000;
1419 fprintf(stdout, "Seed set to %d\n", seed);
1426 fprintf(stdout, "mapped writes DISABLED\n");
1429 o_direct = O_DIRECT;
1441 signal(SIGHUP, cleanup);
1442 signal(SIGINT, cleanup);
1443 signal(SIGPIPE, cleanup);
1444 signal(SIGALRM, cleanup);
1445 signal(SIGTERM, cleanup);
1446 signal(SIGXCPU, cleanup);
1447 signal(SIGXFSZ, cleanup);
1448 signal(SIGVTALRM, cleanup);
1449 signal(SIGUSR1, cleanup);
1450 signal(SIGUSR2, cleanup);
1452 initstate(seed, state, 256);
1455 O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC)|o_direct, 0666);
1462 xfs_flock64_t resv = { 0 };
1463 #ifdef HAVE_XFS_PLATFORM_DEFS_H
1464 if (!platform_test_xfs_fd(fd)) {
1466 fprintf(stderr, "main: cannot prealloc, non XFS\n");
1470 resv.l_len = maxfilelen;
1471 if ((xfsctl(fname, fd, XFS_IOC_RESVSP, &resv)) < 0) {
1477 strncat(goodfile, fname, 256);
1478 strcat (goodfile, ".fsxgood");
1479 fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
1480 if (fsxgoodfd < 0) {
1484 strncat(logfile, fname, 256);
1485 strcat (logfile, ".fsxlog");
1486 fsxlogf = fopen(logfile, "w");
1487 if (fsxlogf == NULL) {
1499 file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END);
1500 if (file_size == (off_t)-1) {
1502 warn("main: lseek eof");
1505 ret = lseek(fd, (off_t)0, SEEK_SET);
1506 if (ret == (off_t)-1) {
1508 warn("main: lseek 0");
1512 original_buf = (char *) malloc(maxfilelen);
1513 for (i = 0; i < maxfilelen; i++)
1514 original_buf[i] = random() % 256;
1515 good_buf = (char *) malloc(maxfilelen + writebdy);
1516 good_buf = round_up(good_buf, writebdy, 0);
1517 memset(good_buf, '\0', maxfilelen);
1518 temp_buf = (char *) malloc(maxoplen + readbdy);
1519 temp_buf = round_up(temp_buf, readbdy, 0);
1520 memset(temp_buf, '\0', maxoplen);
1521 if (lite) { /* zero entire existing file */
1524 written = write(fd, good_buf, (size_t)maxfilelen);
1525 if (written != maxfilelen) {
1526 if (written == -1) {
1528 warn("main: error on write");
1530 warn("main: short write, 0x%x bytes instead "
1542 while (numops == -1 || numops--)
1549 prt("All operations completed A-OK!\n");