common/xfs: extract minimum log size message from mkfs correctly
[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 static int openflags = O_RDWR;
16
17 /*
18  * Just creates a random file, overwriting the file in a random number of loops
19  * and fsyncing between each loop.
20  */
21 static int test_one(int *max_blocks)
22 {
23         int loops = (random() % 20) + 5;
24
25         lseek(test_fd, 0, SEEK_SET);
26         while (loops--) {
27                 int character = (random() % 126) + 33; /* printable character */
28                 int blocks = (random() % 100) + 1;
29
30                 if (blocks > *max_blocks)
31                         *max_blocks = blocks;
32                 lseek(test_fd, 0, SEEK_SET);
33                 memset(buf, character, 4096);
34                 if (fsync(test_fd)) {
35                         fprintf(stderr, "Fsync failed, test results will be "
36                                 "invalid: %d\n", errno);
37                         return 1;
38                 }
39
40                 while (blocks--) {
41                         if (write(test_fd, buf, 4096) < 4096) {
42                                 fprintf(stderr, "Short write %d\n", errno);
43                                 return 1;
44                         }
45                 }
46         }
47
48         return 0;
49 }
50
51 /*
52  * Preallocate a randomly sized file and then overwrite the entire thing and
53  * then fsync.
54  */
55 static int test_two(int *max_blocks)
56 {
57         int blocks = (random() % 1024) + 1;
58         int character = (random() % 126) + 33;
59
60         *max_blocks = blocks;
61
62         if (fallocate(test_fd, 0, 0, blocks * 4096)) {
63                 fprintf(stderr, "Error fallocating %d (%s)\n", errno,
64                         strerror(errno));
65                 return 1;
66         }
67
68         lseek(test_fd, 0, SEEK_SET);
69         memset(buf, character, 4096);
70         while (blocks--) {
71                 if (write(test_fd, buf, 4096) < 4096) {
72                         fprintf(stderr, "Short write %d\n", errno);
73                         return 1;
74                 }
75         }
76
77         return 0;
78 }
79
80 static void drop_all_caches()
81 {
82         char value[] = "3\n";
83         int fd;
84
85         if ((fd = open("/proc/sys/vm/drop_caches", O_WRONLY)) < 0) {
86                 fprintf(stderr, "Error opening drop caches: %d\n", errno);
87                 return;
88         }
89
90         write(fd, value, sizeof(value)-1);
91         close(fd);
92 }
93
94 /*
95  * Randomly write inside of a file, either creating a sparse file or prealloc
96  * the file and randomly write within it, depending on the prealloc flag
97  */
98 static int test_three(int *max_blocks, int prealloc, int rand_fsync,
99                       int do_sync, int drop_caches)
100 {
101         int size = (random() % 2048) + 4;
102         int blocks = size / 2;
103         int sync_block = blocks / 2;
104         int rand_sync_interval = (random() % blocks) + 1;
105         int character = (random() % 126) + 33;
106
107         if (prealloc && fallocate(test_fd, 0, 0, size * 4096)) {
108                 fprintf(stderr, "Error fallocating %d (%s)\n", errno,
109                         strerror(errno));
110                 return 1;
111         }
112
113         if (prealloc)
114                 *max_blocks = size;
115
116         memset(buf, character, 4096);
117         while (blocks--) {
118                 int block = (random() % size);
119
120                 if ((block + 1) > *max_blocks)
121                         *max_blocks = block + 1;
122
123                 if (rand_fsync && !(blocks % rand_sync_interval)) {
124                         if (fsync(test_fd)) {
125                                 fprintf(stderr, "Fsync failed, test results "
126                                         "will be invalid: %d\n", errno);
127                                 return 1;
128                         }
129                 }
130
131                 /* Force a transaction commit in between just for fun */
132                 if (blocks == sync_block && (do_sync || drop_caches)) {
133                         if (do_sync)
134                                 sync();
135                         else
136                                 sync_file_range(test_fd, 0, 0,
137                                                 SYNC_FILE_RANGE_WRITE|
138                                                 SYNC_FILE_RANGE_WAIT_AFTER);
139
140                         if (drop_caches) {
141                                 close(test_fd);
142                                 drop_all_caches();
143                                 test_fd = open(fname, openflags);
144                                 if (test_fd < 0) {
145                                         test_fd = 0;
146                                         fprintf(stderr, "Error re-opening file: %d\n",
147                                                 errno);
148                                         return 1;
149                                 }
150                         }
151                 }
152
153                 if (pwrite(test_fd, buf, 4096, block * 4096) < 4096) {
154                         fprintf(stderr, "Short write %d\n", errno);
155                         return 1;
156                 }
157         }
158
159         return 0;
160 }
161
162 static void timeval_subtract(struct timeval *result,struct timeval *x,
163                              struct timeval *y)
164 {
165         if (x->tv_usec < y->tv_usec) {
166                 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
167                 y->tv_usec -= 1000000 * nsec;
168                 y->tv_sec += nsec;
169         }
170
171         if (x->tv_usec - y->tv_usec > 1000000) {
172                 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
173                 y->tv_usec += 1000000 * nsec;
174                 y->tv_sec -= nsec;
175         }
176
177         result->tv_sec = x->tv_sec - y->tv_sec;
178         result->tv_usec = x->tv_usec - y->tv_usec;
179 }
180
181 static int test_four(int *max_blocks)
182 {
183         size_t size = 2621440;  /* 10 gigabytes */
184         size_t blocks = size / 2;
185         size_t sync_block = blocks / 8; /* fsync 8 times */
186         int character = (random() % 126) + 33;
187         struct timeval start, end, diff;
188
189         memset(buf, character, 4096);
190         while (blocks--) {
191                 off_t block = (random() % size);
192
193                 if ((block + 1) > *max_blocks)
194                         *max_blocks = block + 1;
195
196                 if ((blocks % sync_block) == 0) {
197                         if (gettimeofday(&start, NULL)) {
198                                 fprintf(stderr, "Error getting time: %d\n",
199                                         errno);
200                                 return 1;
201                         }
202                         if (fsync(test_fd)) {
203                                 fprintf(stderr, "Fsync failed, test results "
204                                         "will be invalid: %d\n", errno);
205                                 return 1;
206                         }
207                         if (gettimeofday(&end, NULL)) {
208                                 fprintf(stderr, "Error getting time: %d\n",
209                                         errno);
210                                 return 1;
211                         }
212                         timeval_subtract(&diff, &end, &start);
213                         printf("Fsync time was %ds and %dus\n",
214                                (int)diff.tv_sec, (int)diff.tv_usec);
215                 }
216
217                 if (pwrite(test_fd, buf, 4096, block * 4096) < 4096) {
218                         fprintf(stderr, "Short write %d\n", errno);
219                         return 1;
220                 }
221         }
222
223         return 0;
224 }
225
226 static int test_five()
227 {
228         int character = (random() % 126) + 33;
229         int runs = (random() % 100) + 1;
230         int i;
231
232         memset(buf, character, 3072);
233         for (i = 0; i < runs; i++) {
234                 ssize_t write_size = (random() % 3072) + 1;
235
236                 if (pwrite(test_fd, buf, write_size, 0) < write_size) {
237                         fprintf(stderr, "Short write %d\n", errno);
238                         return 1;
239                 }
240
241                 if ((i % 8) == 0) {
242                         if (fsync(test_fd)) {
243                                 fprintf(stderr, "Fsync failed, test results "
244                                         "will be invalid: %d\n", errno);
245                                 return 1;
246                         }
247                 }
248         }
249
250         return 0;
251 }
252
253 /*
254  * Reproducer for something like this
255  *
256  * [data][prealloc][data]
257  *
258  * and then in the [prealloc] section we have
259  *
260  * [ pre ][pre][     pre     ]
261  * [d][pp][dd][ppp][d][ppp][d]
262  *
263  * where each letter represents on block of either data or prealloc.
264  *
265  * This explains all the weirdly specific numbers.
266  */
267 static int test_six()
268 {
269         int character = (random() % 126) + 33;
270         int i;
271
272         memset(buf, character, 4096);
273
274         /* Write on either side of the file, leaving a hole in the middle */
275         for (i = 0; i < 10; i++) {
276                 if (pwrite(test_fd, buf, 4096, i * 4096) < 4096) {
277                         fprintf(stderr, "Short write %d\n", errno);
278                         return 1;
279                 }
280         }
281
282         if (fsync(test_fd)) {
283                 fprintf(stderr, "Fsync failed %d\n", errno);
284                 return 1;
285         }
286
287         /*
288          * The test fs I had the prealloc extent was 13 4k blocks long so I'm
289          * just using that to give myself the best chances of reproducing.
290          */
291         for (i = 23; i < 33; i++) {
292                 if (pwrite(test_fd, buf, 4096, i * 4096) < 4096) {
293                         fprintf(stderr, "Short write %d\n", errno);
294                         return 1;
295                 }
296         }
297
298         if (fsync(test_fd)) {
299                 fprintf(stderr, "Fsync failed %d\n", errno);
300                 return 1;
301         }
302
303         if (fallocate(test_fd, 0, 10 * 4096, 4 * 4096)) {
304                 fprintf(stderr, "Error fallocating %d\n", errno);
305                 return 1;
306         }
307
308         if (fallocate(test_fd, 0, 14 * 4096, 5 * 4096)) {
309                 fprintf(stderr, "Error fallocating %d\n", errno);
310                 return 1;
311         }
312
313         if (fallocate(test_fd, 0, 19 * 4096, 4 * 4096)) {
314                 fprintf(stderr, "Error fallocating %d\n", errno);
315                 return 1;
316         }
317
318         if (pwrite(test_fd, buf, 4096, 10 * 4096) < 4096) {
319                 fprintf(stderr, "Short write %d\n", errno);
320                 return 1;
321         }
322
323         if (fsync(test_fd)) {
324                 fprintf(stderr, "Fsync failed %d\n", errno);
325                 return 1;
326         }
327
328         for (i = 13; i < 15; i++) {
329                 if (pwrite(test_fd, buf, 4096, i * 4096) < 4096) {
330                         fprintf(stderr, "Short write %d\n", errno);
331                         return 1;
332                 }
333         }
334
335         if (fsync(test_fd)) {
336                 fprintf(stderr, "Fsync failed %d\n", errno);
337                 return 1;
338         }
339
340         if (pwrite(test_fd, buf, 4096, 18 * 4096) < 4096) {
341                 fprintf(stderr, "Short write %d\n", errno);
342                 return 1;
343         }
344
345         if (fsync(test_fd)) {
346                 fprintf(stderr, "Fsync failed %d\n", errno);
347                 return 1;
348         }
349
350         if (pwrite(test_fd, buf, 4096, 22 * 4096) < 4096) {
351                 fprintf(stderr, "Short write %d\n", errno);
352                 return 1;
353         }
354
355         if (fsync(test_fd)) {
356                 fprintf(stderr, "Fsync failed %d\n", errno);
357                 return 1;
358         }
359
360         return 0;
361 }
362
363 static void usage()
364 {
365         printf("Usage fsync-tester [-s <seed>] [-r] [-d] -t <test-num> <filename>\n");
366         printf("   -s seed   : seed for teh random map generator (defaults to reading /dev/urandom)\n");
367         printf("   -r        : don't reboot the box immediately\n");
368         printf("   -d        : use O_DIRECT\n");
369         printf("   -t test   : test nr to run, required\n");
370         exit(1);
371 }
372
373 int main(int argc, char **argv)
374 {
375         int opt;
376         int fd;
377         int max_blocks = 0;
378         char *endptr;
379         unsigned int seed = 123;
380         int reboot = 0;
381         int direct_io = 0;
382         long int test = 1;
383         long int tmp;
384         int ret = 0;
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                 openflags |= 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, openflags | O_CREAT | O_TRUNC, 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 }