btrfs/036: fix sporadic failures when unmounting scratch filesystem
[xfstests-dev.git] / src / randholes.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2000-2003, 2010 SGI
4  * All Rights Reserved.
5  */
6  
7 #include <inttypes.h>
8
9 #include "global.h"
10
11 #define power_of_2(x)   ((x) && !((x) & ((x) - 1)))
12 #define DEFAULT_FILESIZE        ((uint64_t) (256 * 1024 * 1024))
13 #define DEFAULT_BLOCKSIZE       512
14
15 #define SETBIT(ARRAY, N)        ((ARRAY)[(N)/8] |= (1 << ((N)%8)))
16 #define BITVAL(ARRAY, N)        ((ARRAY)[(N)/8] & (1 << ((N)%8)))
17
18 /* Bit-vector array showing which blocks have been written */
19 static unsigned char    *valid;
20
21 static uint64_t filesize;
22 static uint64_t fileoffset;
23
24 static unsigned int blocksize;
25 static int count;
26 static int verbose;
27 static int wsync;
28 static int direct;
29 static int alloconly;
30 static int rt;
31 static int extsize;     /* used only for real-time */
32 static int preserve;
33 static int test;
34
35 #define READ_XFER       256     /* blocks to read at a time when checking */
36
37 /*
38  * Define xfscntl() to mask the difference between the Linux
39  * and the Irix fcntl() interfaces to XFS for user space.  The
40  * "cmd" argument is just the last part of the command, e.g.
41  * pass FSGETXATTR in place of either XFS_IOC_FSGETXATTR (Linux)
42  * F_FSGETXATTR (Irix).
43  *
44  */
45 #  define xfscntl(filename, fd, cmd, arg) \
46                 xfsctl((filename), (fd), XFS_IOC_ ## cmd, (arg))
47
48 static void
49 usage(char *progname)
50 {
51         fprintf(stderr,
52                 "usage: %s [-l filesize] [-b blocksize] [-c count]\n"
53                 "\t\t[-o write_offset] [-s seed] [-r [-x extentsize]]\n"
54                 "\t\t[-w] [-v] [-d] [-a] [-p] [-t] filename\n\n",
55                 progname);
56         fprintf(stderr, "\tdefault filesize is %" PRIu64 " bytes\n",
57                 DEFAULT_FILESIZE);
58         fprintf(stderr, "\tdefault blocksize is %u bytes\n",
59                 DEFAULT_BLOCKSIZE);
60         fprintf(stderr, "\tdefault count is %d block-sized writes\n",
61                 (int) (DEFAULT_FILESIZE / DEFAULT_BLOCKSIZE));
62         fprintf(stderr, "\tdefault write_offset is %" PRIu64 " bytes\n",
63                 (uint64_t) 0);
64         exit(1);
65 }
66
67 /* Returns filename if successful or a null pointer if an error occurs */
68 static char *
69 parseargs(int argc, char *argv[])
70 {
71         int seed;
72         int ch;
73
74         filesize = DEFAULT_FILESIZE;
75         blocksize = DEFAULT_BLOCKSIZE;
76         count = (int) filesize / blocksize;
77         verbose = 0;
78         wsync = 0;
79         seed = time(NULL);
80         test = 0;
81         while ((ch = getopt(argc, argv, "b:l:s:c:o:x:vwdrapt")) != EOF) {
82                 switch(ch) {
83                 case 'b':       blocksize  = atoi(optarg);      break;
84                 case 'l':       filesize   = strtoull(optarg, NULL, 16); break;
85                 case 's':       seed       = atoi(optarg);      break;
86                 case 'c':       count      = atoi(optarg);      break;
87                 case 'o':       fileoffset = strtoull(optarg, NULL, 16); break;
88                 case 'x':       extsize    = atoi(optarg);      break;
89                 case 'v':       verbose++;                      break;
90                 case 'w':       wsync++;                        break;
91                 case 'd':       direct++;                       break;
92                 case 'r':       rt++; direct++;                 break;
93                 case 'a':       alloconly++;                    break;
94                 case 'p':       preserve++;                     break;
95                 case 't':       test++; preserve++;             break;
96                 default:        usage(argv[0]);                 break;
97                 }
98         }
99         if (optind != argc - 1)
100                 usage(argv[0]);
101
102         if ((filesize % blocksize) != 0) {
103                 filesize -= filesize % blocksize;
104                 printf("filesize not a multiple of blocksize, "
105                         "reducing filesize to %llu\n",
106                        (unsigned long long) filesize);
107         }
108         if ((fileoffset % blocksize) != 0) {
109                 fileoffset -= fileoffset % blocksize;
110                 printf("fileoffset not a multiple of blocksize, "
111                         "reducing fileoffset to %llu\n",
112                        (unsigned long long) fileoffset);
113         }
114         if (count > (filesize/blocksize)) {
115                 count = (filesize/blocksize);
116                 printf("count of blocks written is too large, "
117                         "setting to %d\n", count);
118         } else if (count < 1) {
119                 count = 1;
120                 printf("count of blocks written is too small, "
121                         "setting to %d\n", count);
122         }
123         printf("randholes: Seed = %d (use \"-s %d\" "
124                 "to re-execute this test)\n", seed, seed);
125         srandom(seed);
126
127         printf("randholes: blocksize=%d, filesize=%llu, seed=%d\n"
128                "randholes: count=%d, offset=%llu, extsize=%d\n",
129                 blocksize, (unsigned long long)filesize, seed,
130                count, (unsigned long long)fileoffset, extsize);
131         printf("randholes: verbose=%d, wsync=%d, direct=%d, "
132                 "rt=%d, alloconly=%d, preserve=%d, test=%d\n",
133                 verbose, wsync, direct ? 1 : 0, rt, alloconly, preserve, test);
134
135         /* Last argument is the file name.  Return it. */
136
137         return argv[optind];    /* Success */
138 }
139
140 /*
141  * Determine the next random block number to which to write.
142  * If an already-written block is selected, choose the next
143  * unused higher-numbered block.  Returns the block number,
144  * or -1 if we exhaust available blocks looking for an unused
145  * one.
146  */
147 static int
148 findblock(void)
149 {
150         int block, numblocks;
151
152         numblocks = filesize / blocksize;
153         block = random() % numblocks;
154
155         while (BITVAL(valid, block)) {
156                 if (++block == numblocks) {
157                         printf("returning block -1\n");
158                         return -1;
159                 }
160         }
161         return block;
162 }
163
164 static void
165 dumpblock(int *buffer, uint64_t offset, int blocksize)
166 {
167         int     i;
168
169         for (i = 0; i < (blocksize / 16); i++) {
170                 printf("%llx: 0x%08x 0x%08x 0x%08x 0x%08x\n",
171                        (unsigned long long) offset, *buffer, *(buffer + 1),
172                        *(buffer + 2), *(buffer + 3));
173                 offset += 16;
174                 buffer += 4;
175         }
176 }
177
178 static void
179 writeblks(char *fname, int fd, size_t alignment)
180 {
181         uint64_t offset;
182         char *buffer = NULL;
183         int block;
184         int ret;
185         struct flock64 fl;
186
187         if (!test) {
188                 ret = posix_memalign((void **) &buffer, alignment, blocksize);
189                 if (ret) {
190                         fprintf(stderr, "posix_memalign: %s\n", strerror(ret));
191                         exit(1);
192                 }
193                 memset(buffer, 0, blocksize);
194         }
195
196         /*
197          * Avoid allocation patterns being perturbed by different speculative
198          * preallocation beyond EOF configurations by first truncating the file
199          * to the expected maximum file size.
200          */
201         if (ftruncate(fd, filesize) < 0) {
202                 perror("ftruncate");
203                 exit(EXIT_FAILURE);
204         }
205
206         do {
207                 if (verbose && ((count % 100) == 0)) {
208                         printf(".");
209                         fflush(stdout);
210                 }
211                 block = findblock();
212                 if (block < 0) {
213                     perror("findblock");
214                     exit(1);
215                 }
216
217                 offset = (uint64_t) block * blocksize;
218                 if (alloconly) {
219                         if (test) continue;
220                         
221                         fl.l_start = fileoffset + offset;
222                         fl.l_len = blocksize;
223                         fl.l_whence = 0;
224
225                         if (xfscntl(fname, fd, RESVSP64, &fl) < 0) {
226                                 perror("xfsnctl(RESVSP64)");
227                                 exit(1);
228                         }
229                         continue;
230                 }
231                 SETBIT(valid, block);
232                 if (!test) {
233                         if (lseek64(fd, fileoffset + offset, SEEK_SET) < 0) {
234                                 perror("lseek");
235                                 exit(1);
236                         }
237                         /*
238                          * Before writing, record offset at the base
239                          * of the buffer and at offset 256 bytes
240                          * into it.  We'll verify this when we read
241                          * it back in again.
242                          */
243                         *(uint64_t *) buffer = fileoffset + offset;
244                         *(uint64_t *) (buffer + 256) = fileoffset + offset;
245
246                         if (write(fd, buffer, blocksize) < blocksize) {
247                                 perror("write");
248                                 exit(1);
249                         }
250                 }
251                 if (verbose > 1) {
252                         printf("%swriting data at offset=%llx\n",
253                                 test ? "NOT " : "",
254                                 (unsigned long long) (fileoffset + offset));
255                 }
256         } while (--count);
257
258         free(buffer);
259 }
260
261 static int
262 readblks(int fd, size_t alignment)
263 {
264         uint64_t offset;
265         char *buffer, *tmp;
266         unsigned int xfer, block, i;
267         int err=0;
268
269         if (alloconly)
270                 return 0;
271         xfer = READ_XFER*blocksize;
272         err = posix_memalign((void **) &buffer, alignment, xfer);
273         if (err) {
274                 fprintf(stderr, "posix_memalign: %s\n", strerror(err));
275                 exit(1);
276         }
277         memset(buffer, 0, xfer);
278         if (verbose)
279                 printf("\n");
280
281         if (lseek64(fd, fileoffset, SEEK_SET) < 0) {
282                 perror("lseek");
283                 exit(1);
284         }
285         block = 0;
286         offset = 0;
287         while (offset < filesize) {
288                 if ((i = read(fd, buffer, xfer) < xfer)) {
289                         if (i < 2)
290                                 break;
291                         perror("read");
292                         exit(1);
293                 }
294                 tmp = buffer;
295                 for (i = 0; i < READ_XFER; i++) {
296                         uint64_t want;
297                         uint64_t first;
298                         uint64_t second;
299
300                         if (verbose && ((block % 100) == 0)) {
301                                 printf("+");
302                                 fflush(stdout);
303                         }
304
305                         want = BITVAL(valid, block) ? offset : 0;
306                         first = *(uint64_t *) tmp;
307                         second = *(uint64_t *) (tmp + 256);
308                         if (first != want || second != want) {
309                                 printf("mismatched data at offset=0x%" PRIx64
310                                         ", expected 0x%" PRIx64
311                                         ", got 0x%" PRIx64
312                                         " and 0x%" PRIx64 "\n",
313                                          fileoffset + offset, want,
314                                          first, second);
315                                 err++;
316                         }
317                         if (verbose > 2) {
318                                 printf("block %d blocksize %d\n", block,
319                                        blocksize);
320                                 dumpblock((int *)tmp, fileoffset + offset,
321                                           blocksize);
322                         }
323
324                         block++;
325                         offset += blocksize;
326                         tmp += blocksize;
327                 }
328         }
329         if (verbose)
330                 printf("\n");
331
332         free(buffer);
333         return err;
334 }
335
336 /*
337  * Determine the memory alignment required for I/O buffers.  For
338  * direct I/O we request the needed information from the file
339  * system; otherwise pointer alignment is fine.  Returns the
340  * alignment multiple, or 0 if an error occurs.
341  */
342 static size_t
343 get_alignment(char *filename, int fd)
344 {
345         struct dioattr dioattr;
346
347         if (! direct)
348                 return sizeof (void *);
349
350         memset(&dioattr, 0, sizeof dioattr);
351         if (xfscntl(filename, fd, DIOINFO, &dioattr) < 0) {
352                 perror("xfscntl(FIOINFO)");
353                 return 0;
354         }
355
356         /* Make sure the alignment meets the needs of posix_memalign() */
357
358         if (dioattr.d_mem % sizeof (void *) || ! power_of_2(dioattr.d_mem)) {
359                 perror("d_mem bad");
360                 return 0;
361         }
362
363         /*
364          * Also make sure user doesn't specify a block size that's
365          * incompatible with the underlying file system.
366          */
367         if (! dioattr.d_miniosz) {
368                 perror("miniosz == 0!");
369                 return 0;
370         }
371         if (blocksize % dioattr.d_miniosz) {
372                 fprintf(stderr, "blocksize %d must be a multiple of "
373                         "%d for direct I/O\n", blocksize, dioattr.d_miniosz);
374                 return 0;
375         }
376
377         return (size_t) dioattr.d_mem;
378 }
379
380 static int
381 realtime_setup(char *filename, int fd)
382 {
383         struct fsxattr rtattr;
384
385         (void) memset(&rtattr, 0, sizeof rtattr);
386         if (xfscntl(filename, fd, FSGETXATTR, &rtattr) < 0) {
387                 perror("FSGETXATTR)");
388                 return 1;
389         }
390         if ((rtattr.fsx_xflags & XFS_XFLAG_REALTIME) == 0 ||
391             (extsize && rtattr.fsx_extsize != extsize * blocksize)) {
392                 rtattr.fsx_xflags |= XFS_XFLAG_REALTIME;
393                 if (extsize)
394                         rtattr.fsx_extsize = extsize * blocksize;
395                 if (xfscntl(filename, fd, FSSETXATTR, &rtattr) < 0) {
396                         perror("FSSETXATTR)");
397                         return 1;
398                 }
399         }
400
401         return 0;
402 }
403
404 int
405 main(int argc, char *argv[])
406 {
407         char    *filename;
408         size_t  size;
409         int     oflags;
410         int     fd;
411         size_t  alignment;
412         int     errors;
413
414         filename = parseargs(argc, argv);
415         if (! filename)
416                 return 1;
417
418         /*
419          * Allocate a bitmap big enough to track the range of
420          * blocks we'll be dealing with.
421          */
422         size = (filesize / blocksize) / 8 + 1;
423         valid = malloc(size);
424         if ((valid = malloc(size)) == NULL) {
425                 perror("malloc");
426                 return 1;
427         }
428         memset(valid, 0, size);
429
430         /* Lots of arguments affect how we open the file */
431         oflags = test ? O_RDONLY : O_RDWR|O_CREAT;
432         oflags |= preserve ? 0 : O_TRUNC;
433         oflags |= wsync ? O_SYNC : 0;
434         oflags |= direct ? O_DIRECT : 0;
435
436         /*
437          * Open the file, write rand block in random places, read them all
438          * back to check for correctness, then close the file.
439          */
440         if ((fd = open(filename, oflags, 0666)) < 0) {
441                 perror("open");
442                 return 1;
443         }
444         if (rt && realtime_setup(filename, fd))
445                 return 1;
446         alignment = get_alignment(filename, fd);
447         if (! alignment)
448                 return 1;
449
450         printf("write%s\n", test ? " (skipped)" : "");
451         writeblks(filename, fd, alignment);
452
453         printf("readback\n");
454         errors = readblks(fd, alignment);
455
456         if (close(fd) < 0) {
457                 perror("close");
458                 return 1;
459         }
460         free(valid);
461
462         if (errors) {
463                 printf("randholes: %d errors found during readback\n", errors);
464                 return 2;
465         }
466
467         printf("randholes: ok\n");
468
469         return 0;
470 }
471