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.
42 #define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */
46 enum opflags { FL_NONE = 0, FL_SKIPPED = 1, FL_CLOSE_OPEN = 2, FL_KEEP_SIZE = 4 };
49 * A log entry is an operation and a bunch of arguments.
61 struct log_entry oplog[LOGSIZE]; /* the log */
62 int logptr = 0; /* current position in log */
63 int logcount = 0; /* total ops */
66 * The operation matrix is complex due to conditional execution of different
67 * features. Hence when we come to deciding what operation to run, we need to
68 * be careful in how we select the different operations. The active operations
69 * are mapped to numbers as follows:
71 * lite !lite integrity
80 * COLLAPSE RANGE: - 8 8
83 * When mapped read/writes are disabled, they are simply converted to normal
84 * reads and writes. When fallocate/fpunch calls are disabled, they are
87 * Because of the "lite" version, we also need to have different "maximum
88 * operation" defines to allow the ops to be selected correctly based on the
93 /* common operations */
100 /* !lite operations */
101 OP_TRUNCATE = OP_MAX_LITE,
111 /* integrity operations */
112 OP_FSYNC = OP_MAX_FULL,
117 #define PAGE_SIZE getpagesize()
119 #define PAGE_MASK (PAGE_SIZE - 1)
121 char *original_buf; /* a pointer to the original data */
122 char *good_buf; /* a pointer to the correct data */
123 char *temp_buf; /* a pointer to the current data */
124 char *fname; /* name of our test file */
125 char *bname; /* basename of our test file */
126 char *logdev; /* -i flag */
127 char *logid; /* -j flag */
128 char dname[1024]; /* -P flag */
129 char goodfile[PATH_MAX];
130 int dirpath = 0; /* -P flag */
131 int fd; /* fd for our test file */
133 blksize_t block_size = 0;
136 unsigned long testcalls = 0; /* calls to function "test" */
138 unsigned long simulatedopcount = 0; /* -b flag */
139 int closeprob = 0; /* -c flag */
140 int debug = 0; /* -d flag */
141 unsigned long debugstart = 0; /* -D flag */
142 char filldata = 0; /* -g flag */
143 int flush = 0; /* -f flag */
144 int do_fsync = 0; /* -y flag */
145 unsigned long maxfilelen = 256 * 1024; /* -l flag */
146 int sizechecks = 1; /* -n flag disables them */
147 int maxoplen = 64 * 1024; /* -o flag */
148 int quiet = 0; /* -q flag */
149 unsigned long progressinterval = 0; /* -p flag */
150 int readbdy = 1; /* -r flag */
151 int style = 0; /* -s flag */
152 int prealloc = 0; /* -x flag */
153 int truncbdy = 1; /* -t flag */
154 int writebdy = 1; /* -w flag */
155 long monitorstart = -1; /* -m flag */
156 long monitorend = -1; /* -m flag */
157 int lite = 0; /* -L flag */
158 long numops = -1; /* -N flag */
159 int randomoplen = 1; /* -O flag disables it */
160 int seed = 1; /* -S flag */
161 int mapped_writes = 1; /* -W flag disables */
162 int fallocate_calls = 1; /* -F flag disables */
163 int keep_size_calls = 1; /* -K flag disables */
164 int punch_hole_calls = 1; /* -H flag disables */
165 int zero_range_calls = 1; /* -z flag disables */
166 int collapse_range_calls = 1; /* -C flag disables */
167 int insert_range_calls = 1; /* -I flag disables */
168 int mapped_reads = 1; /* -R flag disables it */
169 int check_file = 0; /* -X flag enables */
170 int clone_range_calls = 1; /* -J flag disables */
171 int dedupe_range_calls = 1; /* -B flag disables */
172 int integrity = 0; /* -i flag */
174 int o_direct; /* -Z */
182 int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset);
185 #define fsxread(a,b,c,d) aio_rw(READ, a,b,c,d)
186 #define fsxwrite(a,b,c,d) aio_rw(WRITE, a,b,c,d)
188 #define fsxread(a,b,c,d) read(a,b,c)
189 #define fsxwrite(a,b,c,d) write(a,b,c)
192 const char *replayops = NULL;
193 const char *recordops = NULL;
194 FILE * fsxlogf = NULL;
195 FILE * replayopsf = NULL;
196 char opsfile[PATH_MAX];
200 static void *round_ptr_up(void *ptr, unsigned long align, unsigned long offset)
202 unsigned long ret = (unsigned long)ptr;
204 ret = ((ret + align - 1) & ~(align - 1));
210 vwarnc(int code, const char *fmt, va_list ap)
213 fprintf(stderr, "%s: ", logid);
214 fprintf(stderr, "fsx: ");
216 vfprintf(stderr, fmt, ap);
217 fprintf(stderr, ": ");
219 fprintf(stderr, "%s\n", strerror(code));
223 warn(const char * fmt, ...) {
226 vwarnc(errno, fmt, ap);
231 prt(const char *fmt, ...)
236 fprintf(stdout, "%s: ", logid);
238 vfprintf(stdout, fmt, args);
242 vfprintf(fsxlogf, fmt, args);
248 prterr(const char *prefix)
250 prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
254 static const char *op_names[] = {
256 [OP_WRITE] = "write",
257 [OP_MAPREAD] = "mapread",
258 [OP_MAPWRITE] = "mapwrite",
259 [OP_TRUNCATE] = "truncate",
260 [OP_FALLOCATE] = "fallocate",
261 [OP_PUNCH_HOLE] = "punch_hole",
262 [OP_ZERO_RANGE] = "zero_range",
263 [OP_COLLAPSE_RANGE] = "collapse_range",
264 [OP_INSERT_RANGE] = "insert_range",
265 [OP_CLONE_RANGE] = "clone_range",
266 [OP_DEDUPE_RANGE] = "dedupe_range",
267 [OP_FSYNC] = "fsync",
270 static const char *op_name(int operation)
272 if (operation >= 0 &&
273 operation < sizeof(op_names) / sizeof(op_names[0]))
274 return op_names[operation];
278 static int op_code(const char *name)
282 for (i = 0; i < sizeof(op_names) / sizeof(op_names[0]); i++)
283 if (op_names[i] && strcmp(name, op_names[i]) == 0)
289 log5(int operation, int arg0, int arg1, int arg2, enum opflags flags)
291 struct log_entry *le;
294 le->operation = operation;
296 flags |= FL_CLOSE_OPEN;
300 le->args[3] = file_size;
305 if (logptr >= LOGSIZE)
310 log4(int operation, int arg0, int arg1, enum opflags flags)
312 struct log_entry *le;
315 le->operation = operation;
317 flags |= FL_CLOSE_OPEN;
320 le->args[2] = file_size;
325 if (logptr >= LOGSIZE)
334 struct log_entry *lp;
336 prt("LOG DUMP (%d total operations):\n", logcount);
338 logopsf = fopen(opsfile, "w");
342 if (logcount < LOGSIZE) {
349 for ( ; count > 0; count--) {
350 bool overlap, overlap2;
353 opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE;
354 prt("%d(%3d mod 256): ", opnum, opnum%256);
357 overlap = badoff >= lp->args[0] &&
358 badoff < lp->args[0] + lp->args[1];
360 if (lp->flags & FL_SKIPPED) {
361 prt("SKIPPED (no operation)");
365 switch (lp->operation) {
367 prt("MAPREAD 0x%x thru 0x%x\t(0x%x bytes)",
368 lp->args[0], lp->args[0] + lp->args[1] - 1,
374 prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)",
375 lp->args[0], lp->args[0] + lp->args[1] - 1,
381 prt("READ 0x%x thru 0x%x\t(0x%x bytes)",
382 lp->args[0], lp->args[0] + lp->args[1] - 1,
388 prt("WRITE 0x%x thru 0x%x\t(0x%x bytes)",
389 lp->args[0], lp->args[0] + lp->args[1] - 1,
391 if (lp->args[0] > lp->args[2])
393 else if (lp->args[0] + lp->args[1] > lp->args[2])
395 overlap = (badoff >= lp->args[0] ||
396 badoff >=lp->args[2]) &&
397 badoff < lp->args[0] + lp->args[1];
402 down = lp->args[1] < lp->args[2];
403 prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
404 down ? "DOWN" : "UP", lp->args[2], lp->args[1]);
405 overlap = badoff >= lp->args[1 + !down] &&
406 badoff < lp->args[1 + !!down];
411 /* 0: offset 1: length 2: where alloced */
412 prt("FALLOC 0x%x thru 0x%x\t(0x%x bytes) ",
413 lp->args[0], lp->args[0] + lp->args[1],
415 if (lp->args[0] + lp->args[1] <= lp->args[2])
417 else if (lp->flags & FL_KEEP_SIZE)
425 prt("PUNCH 0x%x thru 0x%x\t(0x%x bytes)",
426 lp->args[0], lp->args[0] + lp->args[1] - 1,
432 prt("ZERO 0x%x thru 0x%x\t(0x%x bytes)",
433 lp->args[0], lp->args[0] + lp->args[1] - 1,
438 case OP_COLLAPSE_RANGE:
439 prt("COLLAPSE 0x%x thru 0x%x\t(0x%x bytes)",
440 lp->args[0], lp->args[0] + lp->args[1] - 1,
445 case OP_INSERT_RANGE:
446 prt("INSERT 0x%x thru 0x%x\t(0x%x bytes)",
447 lp->args[0], lp->args[0] + lp->args[1] - 1,
453 prt("CLONE 0x%x thru 0x%x\t(0x%x bytes) to 0x%x thru 0x%x",
454 lp->args[0], lp->args[0] + lp->args[1] - 1,
456 lp->args[2], lp->args[2] + lp->args[1] - 1);
457 overlap2 = badoff >= lp->args[2] &&
458 badoff < lp->args[2] + lp->args[1];
459 if (overlap && overlap2)
466 case OP_DEDUPE_RANGE:
467 prt("DEDUPE 0x%x thru 0x%x\t(0x%x bytes) to 0x%x thru 0x%x",
468 lp->args[0], lp->args[0] + lp->args[1] - 1,
470 lp->args[2], lp->args[2] + lp->args[1] - 1);
471 overlap2 = badoff >= lp->args[2] &&
472 badoff < lp->args[2] + lp->args[1];
473 if (overlap && overlap2)
484 prt("BOGUS LOG ENTRY (operation code = %d)!",
490 if (lp->flags & FL_CLOSE_OPEN)
491 prt("\n\t\tCLOSE/OPEN");
500 if (lp->flags & FL_SKIPPED)
501 fprintf(logopsf, "skip ");
502 fprintf(logopsf, "%s", op_name(lp->operation));
503 for (j = 0; j < lp->nr_args; j++)
504 fprintf(logopsf, " 0x%x", lp->args[j]);
505 if (lp->flags & FL_KEEP_SIZE)
506 fprintf(logopsf, " keep_size");
507 if (lp->flags & FL_CLOSE_OPEN)
508 fprintf(logopsf, " close_open");
510 fprintf(logopsf, " *");
511 fprintf(logopsf, "\n");
516 if (fclose(logopsf) != 0)
519 prt("Log of operations saved to \"%s\"; "
520 "replay with --replay-ops\n",
527 save_buffer(char *buffer, off_t bufferlength, int fd)
530 ssize_t byteswritten;
532 if (fd <= 0 || bufferlength == 0)
535 if (bufferlength > SSIZE_MAX) {
536 prt("fsx flaw: overflow in save_buffer\n");
540 off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END);
541 if (size_by_seek == (off_t)-1)
542 prterr("save_buffer: lseek eof");
543 else if (bufferlength > size_by_seek) {
544 warn("save_buffer: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek,
545 (unsigned long long)bufferlength);
546 bufferlength = size_by_seek;
550 ret = lseek(fd, (off_t)0, SEEK_SET);
551 if (ret == (off_t)-1)
552 prterr("save_buffer: lseek 0");
554 byteswritten = write(fd, buffer, (size_t)bufferlength);
555 if (byteswritten != bufferlength) {
556 if (byteswritten == -1)
557 prterr("save_buffer write");
559 warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n",
560 (unsigned)byteswritten,
561 (unsigned long long)bufferlength);
567 report_failure(int status)
573 save_buffer(good_buf, file_size, fsxgoodfd);
574 prt("Correct content saved for comparison\n");
575 prt("(maybe hexdump \"%s\" vs \"%s\")\n",
584 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
585 *(((unsigned char *)(cp)) + 1)))
593 snprintf(command, 256, "dmsetup message %s 0 mark %s.mark%d", logdev,
595 ret = system(command);
597 prterr("dmsetup mark failed");
603 dump_fsync_buffer(void)
605 char fname_buffer[PATH_MAX];
611 snprintf(fname_buffer, sizeof(fname_buffer), "%s%s.mark%d", dname,
613 good_fd = open(fname_buffer, O_WRONLY|O_CREAT|O_TRUNC, 0666);
615 prterr(fname_buffer);
619 save_buffer(good_buf, file_size, good_fd);
621 prt("Dumped fsync buffer to %s\n", fname_buffer + dirpath);
625 check_buffers(char *buf, unsigned offset, unsigned size)
633 if (memcmp(good_buf + offset, buf, size) != 0) {
634 prt("READ BAD DATA: offset = 0x%x, size = 0x%x, fname = %s\n",
635 offset, size, fname);
636 prt("OFFSET\tGOOD\tBAD\tRANGE\n");
638 c = good_buf[offset];
642 bad = short_at(&buf[i]);
643 prt("0x%05x\t0x%04x\t0x%04x", offset,
644 short_at(&good_buf[offset]), bad);
645 op = buf[offset & 1 ? i+1 : i];
646 prt("\t0x%05x\n", n);
648 prt("operation# (mod 256) for "
649 "the bad data may be %u\n",
650 ((unsigned)op & 0xff));
652 prt("operation# (mod 256) for "
653 "the bad data unknown, check"
654 " HOLE and EXTEND ops\n");
674 if (fstat(fd, &statbuf)) {
675 prterr("check_size: fstat");
676 statbuf.st_size = -1;
678 size_by_seek = lseek(fd, (off_t)0, SEEK_END);
679 if (file_size != statbuf.st_size || file_size != size_by_seek) {
680 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
681 (unsigned long long)file_size,
682 (unsigned long long)statbuf.st_size,
683 (unsigned long long)size_by_seek);
690 check_trunc_hack(void)
693 off_t offset = file_size + (off_t)100000;
695 if (ftruncate(fd, file_size))
697 if (ftruncate(fd, offset))
700 if (statbuf.st_size != offset) {
701 prt("no extend on truncate! not posix!\n");
704 if (ftruncate(fd, file_size)) {
706 prterr("check_trunc_hack: ftruncate");
712 doflush(unsigned offset, unsigned size)
718 if (o_direct == O_DIRECT)
721 pg_offset = offset & mmap_mask;
722 map_size = pg_offset + size;
724 if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
725 MAP_FILE | MAP_SHARED, fd,
726 (off_t)(offset - pg_offset))) == (char *)-1) {
727 prterr("doflush: mmap");
730 if (msync(p, map_size, MS_INVALIDATE) != 0) {
731 prterr("doflush: msync");
734 if (munmap(p, map_size) != 0) {
735 prterr("doflush: munmap");
741 doread(unsigned offset, unsigned size)
746 offset -= offset % readbdy;
748 size -= size % readbdy;
750 if (!quiet && testcalls > simulatedopcount && !o_direct)
751 prt("skipping zero size read\n");
752 log4(OP_READ, offset, size, FL_SKIPPED);
755 if (size + offset > file_size) {
756 if (!quiet && testcalls > simulatedopcount)
757 prt("skipping seek/read past end of file\n");
758 log4(OP_READ, offset, size, FL_SKIPPED);
762 log4(OP_READ, offset, size, FL_NONE);
764 if (testcalls <= simulatedopcount)
768 ((progressinterval && testcalls % progressinterval == 0) ||
770 (monitorstart == -1 ||
771 (offset + size > monitorstart &&
772 (monitorend == -1 || offset <= monitorend))))))
773 prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
774 offset, offset + size - 1, size);
775 ret = lseek(fd, (off_t)offset, SEEK_SET);
776 if (ret == (off_t)-1) {
777 prterr("doread: lseek");
780 iret = fsxread(fd, temp_buf, size, offset);
783 prterr("doread: read");
785 prt("short read: 0x%x bytes instead of 0x%x\n",
789 check_buffers(temp_buf, offset, size);
793 check_eofpage(char *s, unsigned offset, char *p, int size)
795 unsigned long last_page, should_be_zero;
797 if (offset + size <= (file_size & ~page_mask))
800 * we landed in the last page of the file
801 * test to make sure the VM system provided 0's
802 * beyond the true end of the file mapping
803 * (as required by mmap def in 1996 posix 1003.1)
805 last_page = ((unsigned long)p + (offset & page_mask) + size) & ~page_mask;
807 for (should_be_zero = last_page + (file_size & page_mask);
808 should_be_zero < last_page + page_size;
810 if (*(char *)should_be_zero) {
811 prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n",
812 s, file_size - 1, should_be_zero & page_mask,
813 short_at(should_be_zero));
821 static char *check_buf;
823 unsigned size = file_size;
831 check_buf = (char *) malloc(maxfilelen + writebdy);
832 assert(check_buf != NULL);
833 check_buf = round_ptr_up(check_buf, writebdy, 0);
834 memset(check_buf, '\0', maxfilelen);
838 size -= size % readbdy;
842 ret = lseek(fd, (off_t)offset, SEEK_SET);
843 if (ret == (off_t)-1) {
844 prterr("doread: lseek");
848 iret = fsxread(fd, check_buf, size, offset);
851 prterr("check_contents: read");
853 prt("short check read: 0x%x bytes instead of 0x%x\n",
857 check_buffers(check_buf, offset, size);
859 /* Map eof page, check it */
860 map_offset = size - (size & PAGE_MASK);
861 if (map_offset == size)
862 map_offset -= PAGE_SIZE;
863 map_size = size - map_offset;
865 p = mmap(0, map_size, PROT_READ, MAP_SHARED, fd, map_offset);
866 if (p == MAP_FAILED) {
867 prterr("check_contents: mmap");
870 check_eofpage("check_contents", map_offset, p, map_size);
872 if (munmap(p, map_size) != 0) {
873 prterr("check_contents: munmap");
879 domapread(unsigned offset, unsigned size)
885 offset -= offset % readbdy;
887 if (!quiet && testcalls > simulatedopcount)
888 prt("skipping zero size read\n");
889 log4(OP_MAPREAD, offset, size, FL_SKIPPED);
892 if (size + offset > file_size) {
893 if (!quiet && testcalls > simulatedopcount)
894 prt("skipping seek/read past end of file\n");
895 log4(OP_MAPREAD, offset, size, FL_SKIPPED);
899 log4(OP_MAPREAD, offset, size, FL_NONE);
901 if (testcalls <= simulatedopcount)
905 ((progressinterval && testcalls % progressinterval == 0) ||
907 (monitorstart == -1 ||
908 (offset + size > monitorstart &&
909 (monitorend == -1 || offset <= monitorend))))))
910 prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
911 offset, offset + size - 1, size);
913 pg_offset = offset & PAGE_MASK;
914 map_size = pg_offset + size;
916 if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_SHARED, fd,
917 (off_t)(offset - pg_offset))) == (char *)-1) {
918 prterr("domapread: mmap");
921 memcpy(temp_buf, p + pg_offset, size);
923 check_eofpage("Read", offset, p, size);
925 if (munmap(p, map_size) != 0) {
926 prterr("domapread: munmap");
930 check_buffers(temp_buf, offset, size);
935 gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
939 good_buf[offset] = filldata;
941 good_buf[offset] = testcalls % 256;
943 good_buf[offset] += original_buf[offset];
951 dowrite(unsigned offset, unsigned size)
956 offset -= offset % writebdy;
958 size -= size % writebdy;
960 if (!quiet && testcalls > simulatedopcount && !o_direct)
961 prt("skipping zero size write\n");
962 log4(OP_WRITE, offset, size, FL_SKIPPED);
966 log4(OP_WRITE, offset, size, FL_NONE);
968 gendata(original_buf, good_buf, offset, size);
969 if (file_size < offset + size) {
970 if (file_size < offset)
971 memset(good_buf + file_size, '\0', offset - file_size);
972 file_size = offset + size;
974 warn("Lite file size bug in fsx!");
979 if (testcalls <= simulatedopcount)
983 ((progressinterval && testcalls % progressinterval == 0) ||
985 (monitorstart == -1 ||
986 (offset + size > monitorstart &&
987 (monitorend == -1 || offset <= monitorend))))))
988 prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
989 offset, offset + size - 1, size);
990 ret = lseek(fd, (off_t)offset, SEEK_SET);
991 if (ret == (off_t)-1) {
992 prterr("dowrite: lseek");
995 iret = fsxwrite(fd, good_buf + offset, size, offset);
998 prterr("dowrite: write");
1000 prt("short write: 0x%x bytes instead of 0x%x\n",
1002 report_failure(151);
1006 prt("fsync() failed: %s\n", strerror(errno));
1007 report_failure(152);
1011 doflush(offset, size);
1017 domapwrite(unsigned offset, unsigned size)
1024 offset -= offset % writebdy;
1026 if (!quiet && testcalls > simulatedopcount)
1027 prt("skipping zero size write\n");
1028 log4(OP_MAPWRITE, offset, size, FL_SKIPPED);
1031 cur_filesize = file_size;
1033 log4(OP_MAPWRITE, offset, size, FL_NONE);
1035 gendata(original_buf, good_buf, offset, size);
1036 if (file_size < offset + size) {
1037 if (file_size < offset)
1038 memset(good_buf + file_size, '\0', offset - file_size);
1039 file_size = offset + size;
1041 warn("Lite file size bug in fsx!");
1042 report_failure(200);
1046 if (testcalls <= simulatedopcount)
1050 ((progressinterval && testcalls % progressinterval == 0) ||
1052 (monitorstart == -1 ||
1053 (offset + size > monitorstart &&
1054 (monitorend == -1 || offset <= monitorend))))))
1055 prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
1056 offset, offset + size - 1, size);
1058 if (file_size > cur_filesize) {
1059 if (ftruncate(fd, file_size) == -1) {
1060 prterr("domapwrite: ftruncate");
1064 pg_offset = offset & PAGE_MASK;
1065 map_size = pg_offset + size;
1067 if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
1068 MAP_FILE | MAP_SHARED, fd,
1069 (off_t)(offset - pg_offset))) == (char *)-1) {
1070 prterr("domapwrite: mmap");
1071 report_failure(202);
1073 memcpy(p + pg_offset, good_buf + offset, size);
1074 if (msync(p, map_size, MS_SYNC) != 0) {
1075 prterr("domapwrite: msync");
1076 report_failure(203);
1079 check_eofpage("Write", offset, p, size);
1081 if (munmap(p, map_size) != 0) {
1082 prterr("domapwrite: munmap");
1083 report_failure(204);
1089 dotruncate(unsigned size)
1091 int oldsize = file_size;
1093 size -= size % truncbdy;
1094 if (size > biggest) {
1096 if (!quiet && testcalls > simulatedopcount)
1097 prt("truncating to largest ever: 0x%x\n", size);
1100 log4(OP_TRUNCATE, 0, size, FL_NONE);
1102 if (size > file_size)
1103 memset(good_buf + file_size, '\0', size - file_size);
1106 if (testcalls <= simulatedopcount)
1109 if ((progressinterval && testcalls % progressinterval == 0) ||
1110 (debug && (monitorstart == -1 || monitorend == -1 ||
1111 size <= monitorend)))
1112 prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size);
1113 if (ftruncate(fd, (off_t)size) == -1) {
1114 prt("ftruncate1: %x\n", size);
1115 prterr("dotruncate: ftruncate");
1116 report_failure(160);
1120 #ifdef FALLOC_FL_PUNCH_HOLE
1122 do_punch_hole(unsigned offset, unsigned length)
1124 unsigned end_offset;
1127 int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
1130 if (!quiet && testcalls > simulatedopcount)
1131 prt("skipping zero length punch hole\n");
1132 log4(OP_PUNCH_HOLE, offset, length, FL_SKIPPED);
1136 if (file_size <= (loff_t)offset) {
1137 if (!quiet && testcalls > simulatedopcount)
1138 prt("skipping hole punch off the end of the file\n");
1139 log4(OP_PUNCH_HOLE, offset, length, FL_SKIPPED);
1143 end_offset = offset + length;
1145 log4(OP_PUNCH_HOLE, offset, length, FL_NONE);
1147 if (testcalls <= simulatedopcount)
1150 if ((progressinterval && testcalls % progressinterval == 0) ||
1151 (debug && (monitorstart == -1 || monitorend == -1 ||
1152 end_offset <= monitorend))) {
1153 prt("%lu punch\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
1154 offset, offset+length, length);
1156 if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1157 prt("punch hole: 0x%x to 0x%x\n", offset, offset + length);
1158 prterr("do_punch_hole: fallocate");
1159 report_failure(161);
1163 max_offset = offset < file_size ? offset : file_size;
1164 max_len = max_offset + length <= file_size ? length :
1165 file_size - max_offset;
1166 memset(good_buf + max_offset, '\0', max_len);
1171 do_punch_hole(unsigned offset, unsigned length)
1177 #ifdef FALLOC_FL_ZERO_RANGE
1179 do_zero_range(unsigned offset, unsigned length, int keep_size)
1181 unsigned end_offset;
1182 int mode = FALLOC_FL_ZERO_RANGE;
1185 if (!quiet && testcalls > simulatedopcount)
1186 prt("skipping zero length zero range\n");
1187 log4(OP_ZERO_RANGE, offset, length, FL_SKIPPED |
1188 (keep_size ? FL_KEEP_SIZE : FL_NONE));
1192 end_offset = keep_size ? 0 : offset + length;
1194 if (end_offset > biggest) {
1195 biggest = end_offset;
1196 if (!quiet && testcalls > simulatedopcount)
1197 prt("zero_range to largest ever: 0x%x\n", end_offset);
1201 * last arg matches fallocate string array index in logdump:
1202 * 0: allocate past EOF
1203 * 1: extending prealloc
1204 * 2: interior prealloc
1206 log4(OP_ZERO_RANGE, offset, length,
1207 keep_size ? FL_KEEP_SIZE : FL_NONE);
1209 if (testcalls <= simulatedopcount)
1212 if ((progressinterval && testcalls % progressinterval == 0) ||
1213 (debug && (monitorstart == -1 || monitorend == -1 ||
1214 end_offset <= monitorend))) {
1215 prt("%lu zero\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
1216 offset, offset+length, length);
1218 if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1219 prt("zero range: 0x%x to 0x%x\n", offset, offset + length);
1220 prterr("do_zero_range: fallocate");
1221 report_failure(161);
1224 memset(good_buf + offset, '\0', length);
1229 do_zero_range(unsigned offset, unsigned length, int keep_size)
1235 #ifdef FALLOC_FL_COLLAPSE_RANGE
1237 do_collapse_range(unsigned offset, unsigned length)
1239 unsigned end_offset;
1240 int mode = FALLOC_FL_COLLAPSE_RANGE;
1243 if (!quiet && testcalls > simulatedopcount)
1244 prt("skipping zero length collapse range\n");
1245 log4(OP_COLLAPSE_RANGE, offset, length, FL_SKIPPED);
1249 end_offset = offset + length;
1250 if ((loff_t)end_offset >= file_size) {
1251 if (!quiet && testcalls > simulatedopcount)
1252 prt("skipping collapse range behind EOF\n");
1253 log4(OP_COLLAPSE_RANGE, offset, length, FL_SKIPPED);
1257 log4(OP_COLLAPSE_RANGE, offset, length, FL_NONE);
1259 if (testcalls <= simulatedopcount)
1262 if ((progressinterval && testcalls % progressinterval == 0) ||
1263 (debug && (monitorstart == -1 || monitorend == -1 ||
1264 end_offset <= monitorend))) {
1265 prt("%lu collapse\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
1266 offset, offset+length, length);
1268 if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1269 prt("collapse range: 0x%x to 0x%x\n", offset, offset + length);
1270 prterr("do_collapse_range: fallocate");
1271 report_failure(161);
1274 memmove(good_buf + offset, good_buf + end_offset,
1275 file_size - end_offset);
1276 file_size -= length;
1281 do_collapse_range(unsigned offset, unsigned length)
1287 #ifdef FALLOC_FL_INSERT_RANGE
1289 do_insert_range(unsigned offset, unsigned length)
1291 unsigned end_offset;
1292 int mode = FALLOC_FL_INSERT_RANGE;
1295 if (!quiet && testcalls > simulatedopcount)
1296 prt("skipping zero length insert range\n");
1297 log4(OP_INSERT_RANGE, offset, length, FL_SKIPPED);
1301 if ((loff_t)offset >= file_size) {
1302 if (!quiet && testcalls > simulatedopcount)
1303 prt("skipping insert range behind EOF\n");
1304 log4(OP_INSERT_RANGE, offset, length, FL_SKIPPED);
1308 log4(OP_INSERT_RANGE, offset, length, FL_NONE);
1310 if (testcalls <= simulatedopcount)
1313 end_offset = offset + length;
1314 if ((progressinterval && testcalls % progressinterval == 0) ||
1315 (debug && (monitorstart == -1 || monitorend == -1 ||
1316 end_offset <= monitorend))) {
1317 prt("%lu insert\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
1318 offset, offset+length, length);
1320 if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1321 prt("insert range: 0x%x to 0x%x\n", offset, offset + length);
1322 prterr("do_insert_range: fallocate");
1323 report_failure(161);
1326 memmove(good_buf + end_offset, good_buf + offset,
1327 file_size - offset);
1328 memset(good_buf + offset, '\0', length);
1329 file_size += length;
1334 do_insert_range(unsigned offset, unsigned length)
1342 test_clone_range(void)
1344 struct file_clone_range fcr = {
1348 if (ioctl(fd, FICLONERANGE, &fcr) &&
1349 (errno = EOPNOTSUPP || errno == ENOTTY)) {
1352 "main: filesystem does not support "
1353 "clone range, disabling!\n");
1361 do_clone_range(unsigned offset, unsigned length, unsigned dest)
1363 struct file_clone_range fcr = {
1365 .src_offset = offset,
1366 .src_length = length,
1367 .dest_offset = dest,
1371 if (!quiet && testcalls > simulatedopcount)
1372 prt("skipping zero length clone range\n");
1373 log5(OP_CLONE_RANGE, offset, length, dest, FL_SKIPPED);
1377 if ((loff_t)offset >= file_size) {
1378 if (!quiet && testcalls > simulatedopcount)
1379 prt("skipping clone range behind EOF\n");
1380 log5(OP_CLONE_RANGE, offset, length, dest, FL_SKIPPED);
1384 if (dest + length > biggest) {
1385 biggest = dest + length;
1386 if (!quiet && testcalls > simulatedopcount)
1387 prt("cloning to largest ever: 0x%x\n", dest + length);
1390 log5(OP_CLONE_RANGE, offset, length, dest, FL_NONE);
1392 if (testcalls <= simulatedopcount)
1395 if ((progressinterval && testcalls % progressinterval == 0) ||
1396 (debug && (monitorstart == -1 || monitorend == -1 ||
1397 dest <= monitorstart || dest + length <= monitorend))) {
1398 prt("%lu clone\tfrom 0x%x to 0x%x, (0x%x bytes) at 0x%x\n",
1399 testcalls, offset, offset+length, length, dest);
1402 if (ioctl(fd, FICLONERANGE, &fcr) == -1) {
1403 prt("clone range: 0x%x to 0x%x at 0x%x\n", offset,
1404 offset + length, dest);
1405 prterr("do_clone_range: FICLONERANGE");
1406 report_failure(161);
1409 memcpy(good_buf + dest, good_buf + offset, length);
1410 if (dest > file_size)
1411 memset(good_buf + file_size, '\0', dest - file_size);
1412 if (dest + length > file_size)
1413 file_size = dest + length;
1418 test_clone_range(void)
1424 do_clone_range(unsigned offset, unsigned length, unsigned dest)
1430 #ifdef FIDEDUPERANGE
1432 test_dedupe_range(void)
1434 struct file_dedupe_range *fdr;
1440 fdr = calloc(sizeof(struct file_dedupe_range_info) +
1441 sizeof(struct file_dedupe_range), 1);
1443 prterr("do_dedupe_range: malloc");
1444 report_failure(161);
1447 /* Make sure we have at least two blocks */
1448 new_len = block_size * 2;
1449 if (file_size < new_len && ftruncate(fd, new_len)) {
1450 warn("main: ftruncate");
1454 /* Try to dedupe them */
1455 fdr->src_length = block_size;
1456 fdr->dest_count = 1;
1457 fdr->info[0].dest_fd = fd;
1458 fdr->info[0].dest_offset = block_size;
1460 if (ioctl(fd, FIDEDUPERANGE, fdr))
1462 else if (fdr->info[0].status < 0)
1463 error = -fdr->info[0].status;
1467 if (error == EOPNOTSUPP || error == ENOTTY) {
1470 "main: filesystem does not support "
1471 "dedupe range, disabling!\n");
1475 /* Put the file back the way it was. */
1476 if (file_size < new_len && ftruncate(fd, file_size)) {
1477 warn("main: ftruncate");
1486 do_dedupe_range(unsigned offset, unsigned length, unsigned dest)
1488 struct file_dedupe_range *fdr;
1491 if (!quiet && testcalls > simulatedopcount)
1492 prt("skipping zero length dedupe range\n");
1493 log5(OP_DEDUPE_RANGE, offset, length, dest, FL_SKIPPED);
1497 if ((loff_t)offset >= file_size) {
1498 if (!quiet && testcalls > simulatedopcount)
1499 prt("skipping dedupe range behind EOF\n");
1500 log5(OP_DEDUPE_RANGE, offset, length, dest, FL_SKIPPED);
1504 log5(OP_DEDUPE_RANGE, offset, length, dest, FL_NONE);
1506 if (testcalls <= simulatedopcount)
1509 if ((progressinterval && testcalls % progressinterval == 0) ||
1510 (debug && (monitorstart == -1 || monitorend == -1 ||
1511 dest <= monitorstart || dest + length <= monitorend))) {
1512 prt("%lu dedupe\tfrom 0x%x to 0x%x, (0x%x bytes) at 0x%x\n",
1513 testcalls, offset, offset+length, length, dest);
1517 fdr = calloc(sizeof(struct file_dedupe_range_info) +
1518 sizeof(struct file_dedupe_range), 1);
1520 prterr("do_dedupe_range: malloc");
1521 report_failure(161);
1524 /* Dedupe data blocks */
1525 fdr->src_offset = offset;
1526 fdr->src_length = length;
1527 fdr->dest_count = 1;
1528 fdr->info[0].dest_fd = fd;
1529 fdr->info[0].dest_offset = dest;
1531 if (ioctl(fd, FIDEDUPERANGE, fdr) == -1) {
1532 prt("dedupe range: 0x%x to 0x%x at 0x%x\n", offset,
1533 offset + length, dest);
1534 prterr("do_dedupe_range(0): FIDEDUPERANGE");
1535 report_failure(161);
1536 } else if (fdr->info[0].status < 0) {
1537 errno = -fdr->info[0].status;
1538 prt("dedupe range: 0x%x to 0x%x at 0x%x\n", offset,
1539 offset + length, dest);
1540 prterr("do_dedupe_range(1): FIDEDUPERANGE");
1541 report_failure(161);
1549 test_dedupe_range(void)
1555 do_dedupe_range(unsigned offset, unsigned length, unsigned dest)
1561 #ifdef HAVE_LINUX_FALLOC_H
1562 /* fallocate is basically a no-op unless extending, then a lot like a truncate */
1564 do_preallocate(unsigned offset, unsigned length, int keep_size)
1566 unsigned end_offset;
1569 if (!quiet && testcalls > simulatedopcount)
1570 prt("skipping zero length fallocate\n");
1571 log4(OP_FALLOCATE, offset, length, FL_SKIPPED |
1572 (keep_size ? FL_KEEP_SIZE : FL_NONE));
1576 end_offset = keep_size ? 0 : offset + length;
1578 if (end_offset > biggest) {
1579 biggest = end_offset;
1580 if (!quiet && testcalls > simulatedopcount)
1581 prt("fallocating to largest ever: 0x%x\n", end_offset);
1585 * last arg matches fallocate string array index in logdump:
1586 * 0: allocate past EOF
1587 * 1: extending prealloc
1588 * 2: interior prealloc
1590 log4(OP_FALLOCATE, offset, length,
1591 keep_size ? FL_KEEP_SIZE : FL_NONE);
1593 if (end_offset > file_size) {
1594 memset(good_buf + file_size, '\0', end_offset - file_size);
1595 file_size = end_offset;
1598 if (testcalls <= simulatedopcount)
1601 if ((progressinterval && testcalls % progressinterval == 0) ||
1602 (debug && (monitorstart == -1 || monitorend == -1 ||
1603 end_offset <= monitorend)))
1604 prt("%lu falloc\tfrom 0x%x to 0x%x (0x%x bytes)\n", testcalls,
1605 offset, offset + length, length);
1606 if (fallocate(fd, keep_size ? FALLOC_FL_KEEP_SIZE : 0, (loff_t)offset, (loff_t)length) == -1) {
1607 prt("fallocate: 0x%x to 0x%x\n", offset, offset + length);
1608 prterr("do_preallocate: fallocate");
1609 report_failure(161);
1614 do_preallocate(unsigned offset, unsigned length, int keep_size)
1625 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
1626 prterr("writefileimage: lseek");
1627 report_failure(171);
1629 iret = write(fd, good_buf, file_size);
1630 if ((off_t)iret != file_size) {
1632 prterr("writefileimage: write");
1634 prt("short write: 0x%x bytes instead of 0x%llx\n",
1635 iret, (unsigned long long)file_size);
1636 report_failure(172);
1638 if (lite ? 0 : ftruncate(fd, file_size) == -1) {
1639 prt("ftruncate2: %llx\n", (unsigned long long)file_size);
1640 prterr("writefileimage: ftruncate");
1641 report_failure(173);
1649 if (testcalls <= simulatedopcount)
1653 prt("%lu close/open\n", testcalls);
1655 prterr("docloseopen: close");
1656 report_failure(180);
1658 fd = open(fname, O_RDWR|o_direct, 0);
1660 prterr("docloseopen: open");
1661 report_failure(181);
1670 if (testcalls <= simulatedopcount)
1673 prt("%lu fsync\n", testcalls);
1674 log4(OP_FSYNC, 0, 0, 0);
1678 report_failure(210);
1681 dump_fsync_buffer();
1685 #define TRIM_OFF(off, size) \
1693 #define TRIM_LEN(off, len, size) \
1695 if ((off) + (len) > (size)) \
1696 (len) = (size) - (off); \
1699 #define TRIM_OFF_LEN(off, len, size) \
1701 TRIM_OFF(off, size); \
1702 TRIM_LEN(off, len, size); \
1709 prt("signal %d\n", sig);
1710 prt("testcalls = %lu\n", testcalls);
1715 op_args_count(int operation)
1717 switch (operation) {
1718 case OP_CLONE_RANGE:
1719 case OP_DEDUPE_RANGE:
1727 read_op(struct log_entry *log_entry)
1731 memset(log_entry, 0, sizeof(*log_entry));
1732 log_entry->operation = -1;
1734 while (log_entry->operation == -1) {
1739 if (!fgets(line, sizeof(line), replayopsf)) {
1740 if (feof(replayopsf)) {
1746 str = strtok(line, " \t\n");
1747 } while (!str || str[0] == '#');
1749 if (strcmp(str, "skip") == 0) {
1750 log_entry->flags |= FL_SKIPPED;
1751 str = strtok(NULL, " \t\n");
1755 log_entry->operation = op_code(str);
1756 if (log_entry->operation == -1)
1758 log_entry->nr_args = op_args_count(log_entry->operation);
1759 for (i = 0; i < log_entry->nr_args; i++) {
1762 str = strtok(NULL, " \t\n");
1765 log_entry->args[i] = strtoul(str, &end, 0);
1769 while ((str = strtok(NULL, " \t\n"))) {
1770 if (strcmp(str, "keep_size") == 0)
1771 log_entry->flags |= FL_KEEP_SIZE;
1772 else if (strcmp(str, "close_open") == 0)
1773 log_entry->flags |= FL_CLOSE_OPEN;
1774 else if (strcmp(str, "*") == 0)
1775 ; /* overlap marker; ignore */
1783 fprintf(stderr, "%s: parse error\n", replayops);
1786 cleanup(100); /* doesn't return */
1794 unsigned long offset, offset2;
1800 if (simulatedopcount > 0 && testcalls == simulatedopcount)
1805 if (debugstart > 0 && testcalls >= debugstart)
1808 if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
1809 prt("%lu...\n", testcalls);
1812 struct log_entry log_entry;
1814 while (read_op(&log_entry)) {
1815 if (log_entry.flags & FL_SKIPPED) {
1816 log4(log_entry.operation,
1817 log_entry.args[0], log_entry.args[1],
1822 op = log_entry.operation;
1823 offset = log_entry.args[0];
1824 size = log_entry.args[1];
1825 offset2 = log_entry.args[2];
1826 closeopen = !!(log_entry.flags & FL_CLOSE_OPEN);
1827 keep_size = !!(log_entry.flags & FL_KEEP_SIZE);
1835 closeopen = (rv >> 3) < (1 << 28) / closeprob;
1841 size = random() % (maxoplen + 1);
1843 /* calculate appropriate op to run */
1845 op = rv % OP_MAX_LITE;
1846 else if (!integrity)
1847 op = rv % OP_MAX_FULL;
1849 op = rv % OP_MAX_INTEGRITY;
1854 size = random() % maxfilelen;
1857 if (fallocate_calls && size && keep_size_calls)
1858 keep_size = random() % 2;
1861 if (zero_range_calls && size && keep_size_calls)
1862 keep_size = random() % 2;
1864 case OP_CLONE_RANGE:
1865 TRIM_OFF_LEN(offset, size, file_size);
1866 offset = offset & ~(block_size - 1);
1867 size = size & ~(block_size - 1);
1870 TRIM_OFF(offset2, maxfilelen);
1871 offset2 = offset2 & ~(block_size - 1);
1872 } while (llabs(offset2 - offset) < size ||
1873 offset2 + size > maxfilelen);
1875 case OP_DEDUPE_RANGE:
1879 TRIM_OFF_LEN(offset, size, file_size);
1880 offset = offset & ~(block_size - 1);
1881 size = size & ~(block_size - 1);
1883 if (tries++ >= 30) {
1888 TRIM_OFF(offset2, file_size);
1889 offset2 = offset2 & ~(block_size - 1);
1890 } while (llabs(offset2 - offset) < size ||
1891 offset2 + size > file_size);
1908 if (!fallocate_calls) {
1909 log4(OP_FALLOCATE, offset, size, FL_SKIPPED);
1914 if (!punch_hole_calls) {
1915 log4(OP_PUNCH_HOLE, offset, size, FL_SKIPPED);
1920 if (!zero_range_calls) {
1921 log4(OP_ZERO_RANGE, offset, size, FL_SKIPPED);
1925 case OP_COLLAPSE_RANGE:
1926 if (!collapse_range_calls) {
1927 log4(OP_COLLAPSE_RANGE, offset, size, FL_SKIPPED);
1931 case OP_INSERT_RANGE:
1932 if (!insert_range_calls) {
1933 log4(OP_INSERT_RANGE, offset, size, FL_SKIPPED);
1937 case OP_CLONE_RANGE:
1938 if (!clone_range_calls) {
1939 log5(op, offset, size, offset2, FL_SKIPPED);
1943 case OP_DEDUPE_RANGE:
1944 if (!dedupe_range_calls) {
1945 log5(op, offset, size, offset2, FL_SKIPPED);
1953 TRIM_OFF_LEN(offset, size, file_size);
1954 doread(offset, size);
1958 TRIM_OFF_LEN(offset, size, maxfilelen);
1959 dowrite(offset, size);
1963 TRIM_OFF_LEN(offset, size, file_size);
1964 domapread(offset, size);
1968 TRIM_OFF_LEN(offset, size, maxfilelen);
1969 domapwrite(offset, size);
1977 TRIM_OFF_LEN(offset, size, maxfilelen);
1978 do_preallocate(offset, size, keep_size);
1982 TRIM_OFF_LEN(offset, size, file_size);
1983 do_punch_hole(offset, size);
1986 TRIM_OFF_LEN(offset, size, file_size);
1987 do_zero_range(offset, size, keep_size);
1989 case OP_COLLAPSE_RANGE:
1990 TRIM_OFF_LEN(offset, size, file_size - 1);
1991 offset = offset & ~(block_size - 1);
1992 size = size & ~(block_size - 1);
1994 log4(OP_COLLAPSE_RANGE, offset, size, FL_SKIPPED);
1997 do_collapse_range(offset, size);
1999 case OP_INSERT_RANGE:
2000 TRIM_OFF(offset, file_size);
2001 TRIM_LEN(file_size, size, maxfilelen);
2002 offset = offset & ~(block_size - 1);
2003 size = size & ~(block_size - 1);
2005 log4(OP_INSERT_RANGE, offset, size, FL_SKIPPED);
2008 if (file_size + size > maxfilelen) {
2009 log4(OP_INSERT_RANGE, offset, size, FL_SKIPPED);
2013 do_insert_range(offset, size);
2015 case OP_CLONE_RANGE:
2017 log5(OP_CLONE_RANGE, offset, size, offset2, FL_SKIPPED);
2020 if (offset2 + size > maxfilelen) {
2021 log5(OP_CLONE_RANGE, offset, size, offset2, FL_SKIPPED);
2025 do_clone_range(offset, size, offset2);
2027 case OP_DEDUPE_RANGE:
2029 log5(OP_DEDUPE_RANGE, offset, size, offset2, FL_SKIPPED);
2032 if (offset2 + size > maxfilelen) {
2033 log5(OP_DEDUPE_RANGE, offset, size, offset2, FL_SKIPPED);
2037 do_dedupe_range(offset, size, offset2);
2043 prterr("test: unknown operation");
2048 if (check_file && testcalls > simulatedopcount)
2052 if (sizechecks && testcalls > simulatedopcount)
2063 fprintf(stdout, "usage: %s",
2064 "fsx [-dknqxABFJLOWZ] [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid] [-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\
2065 -b opnum: beginning operation number (default 1)\n\
2066 -c P: 1 in P chance of file close+open at each op (default infinity)\n\
2067 -d: debug output for all operations\n\
2068 -f flush and invalidate cache after I/O\n\
2069 -g X: write character X instead of random generated data\n\
2070 -i logdev: do integrity testing, logdev is the dm log writes device\n\
2071 -j logid: prefix debug log messsages with this id\n\
2072 -k: do not truncate existing file and use its size as upper bound on file size\n\
2073 -l flen: the upper bound on file size (default 262144)\n\
2074 -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\
2075 -n: no verifications of file size\n\
2076 -o oplen: the upper bound on operation size (default 65536)\n\
2077 -p progressinterval: debug output at specified operation interval\n\
2078 -q: quieter operation\n\
2079 -r readbdy: 4096 would make reads page aligned (default 1)\n\
2080 -s style: 1 gives smaller truncates (default 0)\n\
2081 -t truncbdy: 4096 would make truncates page aligned (default 1)\n\
2082 -w writebdy: 4096 would make writes page aligned (default 1)\n\
2083 -x: preallocate file space before starting, XFS only (default 0)\n\
2084 -y synchronize changes to a file\n"
2087 " -A: Use the AIO system calls\n"
2089 " -D startingop: debug output starting at specified operation\n"
2090 #ifdef HAVE_LINUX_FALLOC_H
2091 " -F: Do not use fallocate (preallocation) calls\n"
2093 #ifdef FALLOC_FL_PUNCH_HOLE
2094 " -H: Do not use punch hole calls\n"
2096 #ifdef FALLOC_FL_ZERO_RANGE
2097 " -z: Do not use zero range calls\n"
2099 #ifdef FALLOC_FL_COLLAPSE_RANGE
2100 " -C: Do not use collapse range calls\n"
2102 #ifdef FALLOC_FL_INSERT_RANGE
2103 " -I: Do not use insert range calls\n"
2106 " -J: Do not use clone range calls\n"
2108 #ifdef FIDEDUPERANGE
2109 " -B: Do not use dedupe range calls\n"
2111 " -L: fsxLite - no file creations & no file size changes\n\
2112 -N numops: total # operations to do (default infinity)\n\
2113 -O: use oplen (see -o flag) for every op (default random)\n\
2114 -P: save .fsxlog .fsxops and .fsxgood files in dirpath (default ./)\n\
2115 -S seed: for random # generator (default 1) 0 gets timestamp\n\
2116 -W: mapped write operations DISabled\n\
2117 -X: Read file and compare to good buffer after every operation.\n\
2118 -R: read() system calls only (mapped reads disabled)\n\
2119 -Z: O_DIRECT (use -R, -W, -r and -w too)\n\
2120 --replay-ops opsfile: replay ops from recorded .fsxops file\n\
2121 --record-ops[=opsfile]: dump ops file also on success. optionally specify ops file name\n\
2122 fname: this filename is REQUIRED (no default)\n");
2128 getnum(char *s, char **e)
2133 ret = strtol(s, e, 0);
2163 io_context_t io_ctx;
2169 ret = io_queue_init(QSZ, &io_ctx);
2171 fprintf(stderr, "aio_setup: io_queue_init failed: %s\n",
2179 __aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
2181 struct io_event event;
2182 static struct timespec ts;
2183 struct iocb *iocbs[] = { &iocb };
2188 io_prep_pread(&iocb, fd, buf, len, offset);
2190 io_prep_pwrite(&iocb, fd, buf, len, offset);
2195 ret = io_submit(io_ctx, 1, iocbs);
2197 fprintf(stderr, "errcode=%d\n", ret);
2198 fprintf(stderr, "aio_rw: io_submit failed: %s\n",
2203 ret = io_getevents(io_ctx, 1, 1, &event, &ts);
2206 fprintf(stderr, "aio_rw: no events available\n");
2208 fprintf(stderr, "errcode=%d\n", -ret);
2209 fprintf(stderr, "aio_rw: io_getevents failed: %s\n",
2214 if (len != event.res) {
2216 * The b0rked libaio defines event.res as unsigned.
2217 * However the kernel strucuture has it signed,
2218 * and it's used to pass negated error value.
2219 * Till the library is fixed use the temp var.
2221 res = (long)event.res;
2223 fprintf(stderr, "bad io length: %lu instead of %u\n",
2226 fprintf(stderr, "errcode=%ld\n", -res);
2227 fprintf(stderr, "aio_rw: async io failed: %s\n",
2238 * The caller expects error return in traditional libc
2239 * convention, i.e. -1 and the errno set to error.
2245 int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
2250 ret = __aio_rw(rw, fd, buf, len, offset);
2253 ret = read(fd, buf, len);
2255 ret = write(fd, buf, len);
2262 #define test_fallocate(mode) __test_fallocate(mode, #mode)
2265 __test_fallocate(int mode, const char *mode_str)
2267 #ifdef HAVE_LINUX_FALLOC_H
2270 if (fallocate(fd, mode, file_size, 1) && errno == EOPNOTSUPP) {
2273 "main: filesystem does not support "
2274 "fallocate mode %s, disabling!\n",
2278 if (ftruncate(fd, file_size)) {
2279 warn("main: ftruncate");
2288 static struct option longopts[] = {
2289 {"replay-ops", required_argument, 0, 256},
2290 {"record-ops", optional_argument, 0, 255},
2295 main(int argc, char **argv)
2299 char logfile[PATH_MAX];
2300 struct stat statbuf;
2301 int o_flags = O_RDWR|O_CREAT|O_TRUNC;
2306 page_size = getpagesize();
2307 page_mask = page_size - 1;
2308 mmap_mask = page_mask;
2311 setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
2313 while ((ch = getopt_long(argc, argv,
2314 "b:c:dfg:i:j:kl:m:no:p:qr:s:t:w:xyABD:FJKHzCILN:OP:RS:WXZ",
2315 longopts, NULL)) != EOF)
2318 simulatedopcount = getnum(optarg, &endp);
2320 prt("Will begin at operation %ld\n", simulatedopcount);
2321 if (simulatedopcount == 0)
2323 simulatedopcount -= 1;
2326 closeprob = getnum(optarg, &endp);
2328 prt("Chance of close/open is 1 in %d\n", closeprob);
2343 logdev = strdup(optarg);
2350 logid = strdup(optarg);
2357 o_flags &= ~O_TRUNC;
2360 maxfilelen = getnum(optarg, &endp);
2361 if (maxfilelen <= 0)
2365 monitorstart = getnum(optarg, &endp);
2366 if (monitorstart < 0)
2368 if (!endp || *endp++ != ':')
2370 monitorend = getnum(endp, &endp);
2373 if (monitorend == 0)
2374 monitorend = -1; /* aka infinity */
2380 maxoplen = getnum(optarg, &endp);
2385 progressinterval = getnum(optarg, &endp);
2386 if (progressinterval == 0)
2393 readbdy = getnum(optarg, &endp);
2398 style = getnum(optarg, &endp);
2399 if (style < 0 || style > 1)
2403 truncbdy = getnum(optarg, &endp);
2408 writebdy = getnum(optarg, &endp);
2422 debugstart = getnum(optarg, &endp);
2427 fallocate_calls = 0;
2430 keep_size_calls = 0;
2433 punch_hole_calls = 0;
2436 zero_range_calls = 0;
2439 collapse_range_calls = 0;
2442 insert_range_calls = 0;
2445 clone_range_calls = 0;
2448 dedupe_range_calls = 0;
2452 o_flags &= ~(O_CREAT|O_TRUNC);
2455 numops = getnum(optarg, &endp);
2463 strncpy(dname, optarg, sizeof(dname));
2465 dirpath = strlen(dname);
2471 seed = getnum(optarg, &endp);
2473 seed = time(0) % 10000;
2474 seed += (int)getpid();
2482 prt("mapped writes DISABLED\n");
2488 o_direct = O_DIRECT;
2489 o_flags |= O_DIRECT;
2491 case 255: /* --record-ops */
2493 strncpy(opsfile, optarg, sizeof(opsfile));
2494 recordops = opsfile;
2496 case 256: /* --replay-ops */
2508 if (integrity && !dirpath) {
2509 fprintf(stderr, "option -i <logdev> requires -P <dirpath>\n");
2514 tmp = strdup(fname);
2519 bname = basename(tmp);
2521 signal(SIGHUP, cleanup);
2522 signal(SIGINT, cleanup);
2523 signal(SIGPIPE, cleanup);
2524 signal(SIGALRM, cleanup);
2525 signal(SIGTERM, cleanup);
2526 signal(SIGXCPU, cleanup);
2527 signal(SIGXFSZ, cleanup);
2528 signal(SIGVTALRM, cleanup);
2529 signal(SIGUSR1, cleanup);
2530 signal(SIGUSR2, cleanup);
2533 prt("Seed set to %d\n", seed);
2535 fd = open(fname, o_flags, 0666);
2540 if (fstat(fd, &statbuf)) {
2541 prterr("check_size: fstat");
2544 block_size = statbuf.st_blksize;
2547 xfs_flock64_t resv = { 0 };
2548 #ifdef HAVE_XFS_PLATFORM_DEFS_H
2549 if (!platform_test_xfs_fd(fd)) {
2551 fprintf(stderr, "main: cannot prealloc, non XFS\n");
2555 resv.l_len = maxfilelen;
2556 if ((xfsctl(fname, fd, XFS_IOC_RESVSP, &resv)) < 0) {
2564 snprintf(goodfile, sizeof(goodfile), "%s%s.fsxgood", dname, bname);
2565 snprintf(logfile, sizeof(logfile), "%s%s.fsxlog", dname, bname);
2567 snprintf(opsfile, sizeof(opsfile), "%s%s.fsxops", dname, bname);
2569 snprintf(goodfile, sizeof(goodfile), "%s.fsxgood", fname);
2570 snprintf(logfile, sizeof(logfile), "%s.fsxlog", fname);
2572 snprintf(opsfile, sizeof(opsfile), "%s.fsxops", fname);
2574 fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
2575 if (fsxgoodfd < 0) {
2579 fsxlogf = fopen(logfile, "w");
2580 if (fsxlogf == NULL) {
2587 replayopsf = fopen(replayops, "r");
2599 if (!(o_flags & O_TRUNC)) {
2601 file_size = maxfilelen = biggest = lseek(fd, (off_t)0, SEEK_END);
2602 if (file_size == (off_t)-1) {
2604 warn("main: lseek eof");
2607 ret = lseek(fd, (off_t)0, SEEK_SET);
2608 if (ret == (off_t)-1) {
2610 warn("main: lseek 0");
2614 original_buf = (char *) malloc(maxfilelen);
2615 for (i = 0; i < maxfilelen; i++)
2616 original_buf[i] = random() % 256;
2617 good_buf = (char *) malloc(maxfilelen + writebdy);
2618 good_buf = round_ptr_up(good_buf, writebdy, 0);
2619 memset(good_buf, '\0', maxfilelen);
2620 temp_buf = (char *) malloc(maxoplen + readbdy);
2621 temp_buf = round_ptr_up(temp_buf, readbdy, 0);
2622 memset(temp_buf, '\0', maxoplen);
2623 if (lite) { /* zero entire existing file */
2626 written = write(fd, good_buf, (size_t)maxfilelen);
2627 if (written != maxfilelen) {
2628 if (written == -1) {
2630 warn("main: error on write");
2632 warn("main: short write, 0x%x bytes instead "
2639 ssize_t ret, len = file_size;
2643 ret = read(fd, good_buf + off, len);
2646 warn("main: error on read");
2656 if (fallocate_calls)
2657 fallocate_calls = test_fallocate(0);
2658 if (keep_size_calls)
2659 keep_size_calls = test_fallocate(FALLOC_FL_KEEP_SIZE);
2660 if (punch_hole_calls)
2661 punch_hole_calls = test_fallocate(FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE);
2662 if (zero_range_calls)
2663 zero_range_calls = test_fallocate(FALLOC_FL_ZERO_RANGE);
2664 if (collapse_range_calls)
2665 collapse_range_calls = test_fallocate(FALLOC_FL_COLLAPSE_RANGE);
2666 if (insert_range_calls)
2667 insert_range_calls = test_fallocate(FALLOC_FL_INSERT_RANGE);
2668 if (clone_range_calls)
2669 clone_range_calls = test_clone_range();
2670 if (dedupe_range_calls)
2671 dedupe_range_calls = test_dedupe_range();
2673 while (numops == -1 || numops--)
2682 prt("All %lu operations completed A-OK!\n", testcalls);