fsx: Add fallocate collapse range operation
[xfstests-dev.git] / src / fsync-tester.c
1 #include <sys/time.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <limits.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <unistd.h>
11
12 static int test_fd;
13 static char *buf;
14 static char *fname;
15
16 /*
17  * Just creates a random file, overwriting the file in a random number of loops
18  * and fsyncing between each loop.
19  */
20 static int test_one(int *max_blocks)
21 {
22         int loops = (random() % 20) + 5;
23
24         lseek(test_fd, 0, SEEK_SET);
25         while (loops--) {
26                 int character = (random() % 126) + 33; /* printable character */
27                 int blocks = (random() % 100) + 1;
28
29                 if (blocks > *max_blocks)
30                         *max_blocks = blocks;
31                 lseek(test_fd, 0, SEEK_SET);
32                 memset(buf, character, 4096);
33                 if (fsync(test_fd)) {
34                         fprintf(stderr, "Fsync failed, test results will be "
35                                 "invalid: %d\n", errno);
36                         return 1;
37                 }
38
39                 while (blocks--) {
40                         if (write(test_fd, buf, 4096) < 4096) {
41                                 fprintf(stderr, "Short write %d\n", errno);
42                                 return 1;
43                         }
44                 }
45         }
46
47         return 0;
48 }
49
50 /*
51  * Preallocate a randomly sized file and then overwrite the entire thing and
52  * then fsync.
53  */
54 static int test_two(int *max_blocks)
55 {
56         int blocks = (random() % 1024) + 1;
57         int character = (random() % 126) + 33;
58
59         *max_blocks = blocks;
60
61         if (fallocate(test_fd, 0, 0, blocks * 4096)) {
62                 fprintf(stderr, "Error fallocating %d (%s)\n", errno,
63                         strerror(errno));
64                 return 1;
65         }
66
67         lseek(test_fd, 0, SEEK_SET);
68         memset(buf, character, 4096);
69         while (blocks--) {
70                 if (write(test_fd, buf, 4096) < 4096) {
71                         fprintf(stderr, "Short write %d\n", errno);
72                         return 1;
73                 }
74         }
75
76         return 0;
77 }
78
79 static void drop_all_caches()
80 {
81         char value[] = "3\n";
82         int fd;
83
84         if ((fd = open("/proc/sys/vm/drop_caches", O_WRONLY)) < 0) {
85                 fprintf(stderr, "Error opening drop caches: %d\n", errno);
86                 return;
87         }
88
89         write(fd, value, sizeof(value)-1);
90         close(fd);
91 }
92
93 /*
94  * Randomly write inside of a file, either creating a sparse file or prealloc
95  * the file and randomly write within it, depending on the prealloc flag
96  */
97 static int test_three(int *max_blocks, int prealloc, int rand_fsync,
98                       int do_sync, int drop_caches)
99 {
100         int size = (random() % 2048) + 4;
101         int blocks = size / 2;
102         int sync_block = blocks / 2;
103         int rand_sync_interval = (random() % blocks) + 1;
104         int character = (random() % 126) + 33;
105
106         if (prealloc && fallocate(test_fd, 0, 0, size * 4096)) {
107                 fprintf(stderr, "Error fallocating %d (%s)\n", errno,
108                         strerror(errno));
109                 return 1;
110         }
111
112         if (prealloc)
113                 *max_blocks = size;
114
115         memset(buf, character, 4096);
116         while (blocks--) {
117                 int block = (random() % size);
118
119                 if ((block + 1) > *max_blocks)
120                         *max_blocks = block + 1;
121
122                 if (rand_fsync && !(blocks % rand_sync_interval)) {
123                         if (fsync(test_fd)) {
124                                 fprintf(stderr, "Fsync failed, test results "
125                                         "will be invalid: %d\n", errno);
126                                 return 1;
127                         }
128                 }
129
130                 /* Force a transaction commit in between just for fun */
131                 if (blocks == sync_block && (do_sync || drop_caches)) {
132                         if (do_sync)
133                                 sync();
134                         else
135                                 sync_file_range(test_fd, 0, 0,
136                                                 SYNC_FILE_RANGE_WRITE|
137                                                 SYNC_FILE_RANGE_WAIT_AFTER);
138
139                         if (drop_caches) {
140                                 close(test_fd);
141                                 drop_all_caches();
142                                 test_fd = open(fname, O_RDWR);
143                                 if (test_fd < 0) {
144                                         test_fd = 0;
145                                         fprintf(stderr, "Error re-opening file: %d\n",
146                                                 errno);
147                                         return 1;
148                                 }
149                         }
150                 }
151
152                 if (pwrite(test_fd, buf, 4096, block * 4096) < 4096) {
153                         fprintf(stderr, "Short write %d\n", errno);
154                         return 1;
155                 }
156         }
157
158         return 0;
159 }
160
161 static void timeval_subtract(struct timeval *result,struct timeval *x,
162                              struct timeval *y)
163 {
164         if (x->tv_usec < y->tv_usec) {
165                 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
166                 y->tv_usec -= 1000000 * nsec;
167                 y->tv_sec += nsec;
168         }
169
170         if (x->tv_usec - y->tv_usec > 1000000) {
171                 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
172                 y->tv_usec += 1000000 * nsec;
173                 y->tv_sec -= nsec;
174         }
175
176         result->tv_sec = x->tv_sec - y->tv_sec;
177         result->tv_usec = x->tv_usec - y->tv_usec;
178 }
179
180 static int test_four(int *max_blocks)
181 {
182         size_t size = 2621440;  /* 10 gigabytes */
183         size_t blocks = size / 2;
184         size_t sync_block = blocks / 8; /* fsync 8 times */
185         int character = (random() % 126) + 33;
186         struct timeval start, end, diff;
187
188         memset(buf, character, 4096);
189         while (blocks--) {
190                 off_t block = (random() % size);
191
192                 if ((block + 1) > *max_blocks)
193                         *max_blocks = block + 1;
194
195                 if ((blocks % sync_block) == 0) {
196                         if (gettimeofday(&start, NULL)) {
197                                 fprintf(stderr, "Error getting time: %d\n",
198                                         errno);
199                                 return 1;
200                         }
201                         if (fsync(test_fd)) {
202                                 fprintf(stderr, "Fsync failed, test results "
203                                         "will be invalid: %d\n", errno);
204                                 return 1;
205                         }
206                         if (gettimeofday(&end, NULL)) {
207                                 fprintf(stderr, "Error getting time: %d\n",
208                                         errno);
209                                 return 1;
210                         }
211                         timeval_subtract(&diff, &end, &start);
212                         printf("Fsync time was %ds and %dus\n",
213                                (int)diff.tv_sec, (int)diff.tv_usec);
214                 }
215
216                 if (pwrite(test_fd, buf, 4096, block * 4096) < 4096) {
217                         fprintf(stderr, "Short write %d\n", errno);
218                         return 1;
219                 }
220         }
221
222         return 0;
223 }
224
225 static int test_five()
226 {
227         int character = (random() % 126) + 33;
228         int runs = (random() % 100) + 1;
229         int i;
230
231         memset(buf, character, 3072);
232         for (i = 0; i < runs; i++) {
233                 ssize_t write_size = (random() % 3072) + 1;
234
235                 if (pwrite(test_fd, buf, write_size, 0) < write_size) {
236                         fprintf(stderr, "Short write %d\n", errno);
237                         return 1;
238                 }
239
240                 if ((i % 8) == 0) {
241                         if (fsync(test_fd)) {
242                                 fprintf(stderr, "Fsync failed, test results "
243                                         "will be invalid: %d\n", errno);
244                                 return 1;
245                         }
246                 }
247         }
248
249         return 0;
250 }
251
252 /*
253  * Reproducer for something like this
254  *
255  * [data][prealloc][data]
256  *
257  * and then in the [prealloc] section we have
258  *
259  * [ pre ][pre][     pre     ]
260  * [d][pp][dd][ppp][d][ppp][d]
261  *
262  * where each letter represents on block of either data or prealloc.
263  *
264  * This explains all the weirdly specific numbers.
265  */
266 static int test_six()
267 {
268         int character = (random() % 126) + 33;
269         int i;
270
271         memset(buf, character, 4096);
272
273         /* Write on either side of the file, leaving a hole in the middle */
274         for (i = 0; i < 10; i++) {
275                 if (pwrite(test_fd, buf, 4096, i * 4096) < 4096) {
276                         fprintf(stderr, "Short write %d\n", errno);
277                         return 1;
278                 }
279         }
280
281         if (fsync(test_fd)) {
282                 fprintf(stderr, "Fsync failed %d\n", errno);
283                 return 1;
284         }
285
286         /*
287          * The test fs I had the prealloc extent was 13 4k blocks long so I'm
288          * just using that to give myself the best chances of reproducing.
289          */
290         for (i = 23; i < 33; i++) {
291                 if (pwrite(test_fd, buf, 4096, i * 4096) < 4096) {
292                         fprintf(stderr, "Short write %d\n", errno);
293                         return 1;
294                 }
295         }
296
297         if (fsync(test_fd)) {
298                 fprintf(stderr, "Fsync failed %d\n", errno);
299                 return 1;
300         }
301
302         if (fallocate(test_fd, 0, 10 * 4096, 4 * 4096)) {
303                 fprintf(stderr, "Error fallocating %d\n", errno);
304                 return 1;
305         }
306
307         if (fallocate(test_fd, 0, 14 * 4096, 5 * 4096)) {
308                 fprintf(stderr, "Error fallocating %d\n", errno);
309                 return 1;
310         }
311
312         if (fallocate(test_fd, 0, 19 * 4096, 4 * 4096)) {
313                 fprintf(stderr, "Error fallocating %d\n", errno);
314                 return 1;
315         }
316
317         if (pwrite(test_fd, buf, 4096, 10 * 4096) < 4096) {
318                 fprintf(stderr, "Short write %d\n", errno);
319                 return 1;
320         }
321
322         if (fsync(test_fd)) {
323                 fprintf(stderr, "Fsync failed %d\n", errno);
324                 return 1;
325         }
326
327         for (i = 13; i < 15; i++) {
328                 if (pwrite(test_fd, buf, 4096, i * 4096) < 4096) {
329                         fprintf(stderr, "Short write %d\n", errno);
330                         return 1;
331                 }
332         }
333
334         if (fsync(test_fd)) {
335                 fprintf(stderr, "Fsync failed %d\n", errno);
336                 return 1;
337         }
338
339         if (pwrite(test_fd, buf, 4096, 18 * 4096) < 4096) {
340                 fprintf(stderr, "Short write %d\n", errno);
341                 return 1;
342         }
343
344         if (fsync(test_fd)) {
345                 fprintf(stderr, "Fsync failed %d\n", errno);
346                 return 1;
347         }
348
349         if (pwrite(test_fd, buf, 4096, 22 * 4096) < 4096) {
350                 fprintf(stderr, "Short write %d\n", errno);
351                 return 1;
352         }
353
354         if (fsync(test_fd)) {
355                 fprintf(stderr, "Fsync failed %d\n", errno);
356                 return 1;
357         }
358
359         return 0;
360 }
361
362 static void usage()
363 {
364         printf("Usage fsync-tester [-s <seed>] [-r] [-d] -t <test-num> <filename>\n");
365         printf("   -s seed   : seed for teh random map generator (defaults to reading /dev/urandom)\n");
366         printf("   -r        : don't reboot the box immediately\n");
367         printf("   -d        : use O_DIRECT\n");
368         printf("   -t test   : test nr to run, required\n");
369         exit(1);
370 }
371
372 int main(int argc, char **argv)
373 {
374         int opt;
375         int fd;
376         int max_blocks = 0;
377         char *endptr;
378         unsigned int seed = 123;
379         int reboot = 0;
380         int direct_io = 0;
381         long int test = 1;
382         long int tmp;
383         int ret = 0;
384         int flags = O_RDWR|O_CREAT|O_TRUNC;
385
386         if (argc < 2)
387                 usage();
388
389         fd = open("/dev/urandom", O_RDONLY);
390         if (fd >= 0) {
391                 read(fd, &seed, sizeof(seed));
392                 close(fd);
393         }
394
395         while ((opt = getopt(argc, argv, "s:rdt:")) != -1) {
396                 switch (opt) {
397                 case 's':
398                         tmp = strtol(optarg, &endptr, 10);
399                         if (tmp == LONG_MAX || endptr == optarg)
400                                 usage();
401                         seed = tmp;
402                         break;
403                 case 'r':
404                         reboot = 1;
405                         break;
406                 case 'd':
407                         direct_io = 1;
408                         break;
409                 case 't':
410                         test = strtol(optarg, &endptr, 10);
411                         if (test == LONG_MAX || endptr == optarg)
412                                 usage();
413                         break;
414                 default:
415                         usage();
416                 }
417         }
418
419         if (optind >= argc)
420                 usage();
421
422         /*
423          * test 19 is for smaller than blocksize writes to test btrfs's inline
424          * extent fsyncing, so direct_io doesn't make sense and in fact doesn't
425          * work for other file systems, so just disable direct io for this test.
426          */
427         if (test == 19)
428                 direct_io = 0;
429
430         fname = argv[optind];
431         if (!fname)
432                 usage();
433
434         printf("Random seed is %u\n", seed);
435         srandom(seed);
436
437         if (direct_io) {
438                 flags |= O_DIRECT;
439                 ret = posix_memalign((void **)&buf, getpagesize(), 4096);
440                 if (ret) {
441                         fprintf(stderr, "Error allocating buf: %d\n", ret);
442                         return 1;
443                 }
444         } else {
445                 buf = malloc(4096);
446                 if (!buf) {
447                         fprintf(stderr, "Error allocating buf: %d\n", errno);
448                         return 1;
449                 }
450         }
451
452         test_fd = open(fname, flags, 0644);
453         if (test_fd < 0) {
454                 fprintf(stderr, "Error opening file %d (%s)\n", errno,
455                         strerror(errno));
456                 return 1;
457         }
458
459         switch (test) {
460         case 1:
461                 ret = test_one(&max_blocks);
462                 break;
463         case 2:
464                 ret = test_two(&max_blocks);
465                 break;
466         case 3:
467                 ret = test_three(&max_blocks, 0, 0, 0, 0);
468                 break;
469         case 4:
470                 ret = test_three(&max_blocks, 1, 0, 0, 0);
471                 break;
472         case 5:
473                 ret = test_three(&max_blocks, 0, 1, 0, 0);
474                 break;
475         case 6:
476                 ret = test_three(&max_blocks, 1, 1, 0, 0);
477                 break;
478         case 7:
479                 ret = test_three(&max_blocks, 0, 0, 1, 0);
480                 break;
481         case 8:
482                 ret = test_three(&max_blocks, 1, 0, 1, 0);
483                 break;
484         case 9:
485                 ret = test_three(&max_blocks, 0, 1, 1, 0);
486                 break;
487         case 10:
488                 ret = test_three(&max_blocks, 1, 1, 1, 0);
489                 break;
490         case 11:
491                 ret = test_three(&max_blocks, 0, 0, 0, 1);
492                 break;
493         case 12:
494                 ret = test_three(&max_blocks, 0, 1, 0, 1);
495                 break;
496         case 13:
497                 ret = test_three(&max_blocks, 0, 0, 1, 1);
498                 break;
499         case 14:
500                 ret = test_three(&max_blocks, 0, 1, 1, 1);
501                 break;
502         case 15:
503                 ret = test_three(&max_blocks, 1, 0, 0, 1);
504                 break;
505         case 16:
506                 ret = test_three(&max_blocks, 1, 1, 0, 1);
507                 break;
508         case 17:
509                 ret = test_three(&max_blocks, 1, 0, 1, 1);
510                 break;
511         case 18:
512                 ret = test_three(&max_blocks, 1, 1, 1, 1);
513                 break;
514         case 19:
515                 ret = test_five();
516                 break;
517         case 20:
518                 ret = test_six();
519                 break;
520         case 21:
521                 /*
522                  * This is just a perf test, keep moving it down so it's always
523                  * the last test option.
524                  */
525                 reboot = 0;
526                 ret = test_four(&max_blocks);
527                 goto out;
528         default:
529                 usage();
530         }
531
532         if (ret)
533                 goto out;
534
535         if (fsync(test_fd)) {
536                 fprintf(stderr, "Fsync failed, test results will be invalid: "
537                         "%d\n", errno);
538                 return 1;
539         }
540         if (reboot)
541                 system("reboot -fn");
542 out:
543         free(buf);
544         close(test_fd);
545         return ret;
546 }