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