ee5b6b66a0429da07a2ef00c2bba01b0504f8a6f
[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         int ret;
197         struct flock64 fl;
198
199         if (!test) {
200                 ret = posix_memalign((void **) &buffer, alignment, blocksize);
201                 if (ret) {
202                         fprintf(stderr, "posix_memalign: %s\n", strerror(ret));
203                         exit(1);
204                 }
205                 memset(buffer, 0, blocksize);
206         }
207
208         /*
209          * Avoid allocation patterns being perturbed by different speculative
210          * preallocation beyond EOF configurations by first truncating the file
211          * to the expected maximum file size.
212          */
213         if (ftruncate(fd, filesize) < 0) {
214                 perror("ftruncate");
215                 exit(EXIT_FAILURE);
216         }
217
218         do {
219                 if (verbose && ((count % 100) == 0)) {
220                         printf(".");
221                         fflush(stdout);
222                 }
223                 block = findblock();
224                 if (block < 0) {
225                     perror("findblock");
226                     exit(1);
227                 }
228
229                 offset = (__uint64_t) block * blocksize;
230                 if (alloconly) {
231                         if (test) continue;
232                         
233                         fl.l_start = fileoffset + offset;
234                         fl.l_len = blocksize;
235                         fl.l_whence = 0;
236
237                         if (xfscntl(fname, fd, RESVSP64, &fl) < 0) {
238                                 perror("xfsnctl(RESVSP64)");
239                                 exit(1);
240                         }
241                         continue;
242                 }
243                 SETBIT(valid, block);
244                 if (!test) {
245                         if (lseek64(fd, fileoffset + offset, SEEK_SET) < 0) {
246                                 perror("lseek");
247                                 exit(1);
248                         }
249                         /*
250                          * Before writing, record offset at the base
251                          * of the buffer and at offset 256 bytes
252                          * into it.  We'll verify this when we read
253                          * it back in again.
254                          */
255                         *(__uint64_t *) buffer = fileoffset + offset;
256                         *(__uint64_t *) (buffer + 256) = fileoffset + offset;
257
258                         if (write(fd, buffer, blocksize) < blocksize) {
259                                 perror("write");
260                                 exit(1);
261                         }
262                 }
263                 if (verbose > 1) {
264                         printf("%swriting data at offset=%llx\n",
265                                 test ? "NOT " : "",
266                                 (unsigned long long) (fileoffset + offset));
267                 }
268         } while (--count);
269
270         free(buffer);
271 }
272
273 static int
274 readblks(int fd, size_t alignment)
275 {
276         __uint64_t offset;
277         char *buffer, *tmp;
278         unsigned int xfer, block, i;
279         int err=0;
280
281         if (alloconly)
282                 return 0;
283         xfer = READ_XFER*blocksize;
284         err = posix_memalign((void **) &buffer, alignment, xfer);
285         if (err) {
286                 fprintf(stderr, "posix_memalign: %s\n", strerror(err));
287                 exit(1);
288         }
289         memset(buffer, 0, xfer);
290         if (verbose)
291                 printf("\n");
292
293         if (lseek64(fd, fileoffset, SEEK_SET) < 0) {
294                 perror("lseek");
295                 exit(1);
296         }
297         block = 0;
298         offset = 0;
299         while (offset < filesize) {
300                 if ((i = read(fd, buffer, xfer) < xfer)) {
301                         if (i < 2)
302                                 break;
303                         perror("read");
304                         exit(1);
305                 }
306                 tmp = buffer;
307                 for (i = 0; i < READ_XFER; i++) {
308                         __uint64_t want;
309                         __uint64_t first;
310                         __uint64_t second;
311
312                         if (verbose && ((block % 100) == 0)) {
313                                 printf("+");
314                                 fflush(stdout);
315                         }
316
317                         want = BITVAL(valid, block) ? offset : 0;
318                         first = *(__uint64_t *) tmp;
319                         second = *(__uint64_t *) (tmp + 256);
320                         if (first != want || second != want) {
321                                 printf("mismatched data at offset=0x%" PRIx64
322                                         ", expected 0x%" PRIx64
323                                         ", got 0x%" PRIx64
324                                         " and 0x%" PRIx64 "\n",
325                                          fileoffset + offset, want,
326                                          first, second);
327                                 err++;
328                         }
329                         if (verbose > 2) {
330                                 printf("block %d blocksize %d\n", block,
331                                        blocksize);
332                                 dumpblock((int *)tmp, fileoffset + offset,
333                                           blocksize);
334                         }
335
336                         block++;
337                         offset += blocksize;
338                         tmp += blocksize;
339                 }
340         }
341         if (verbose)
342                 printf("\n");
343
344         free(buffer);
345         return err;
346 }
347
348 /*
349  * Determine the memory alignment required for I/O buffers.  For
350  * direct I/O we request the needed information from the file
351  * system; otherwise pointer alignment is fine.  Returns the
352  * alignment multiple, or 0 if an error occurs.
353  */
354 static size_t
355 get_alignment(char *filename, int fd)
356 {
357         struct dioattr dioattr;
358
359         if (! direct)
360                 return sizeof (void *);
361
362         memset(&dioattr, 0, sizeof dioattr);
363         if (xfscntl(filename, fd, DIOINFO, &dioattr) < 0) {
364                 perror("xfscntl(FIOINFO)");
365                 return 0;
366         }
367
368         /* Make sure the alignment meets the needs of posix_memalign() */
369
370         if (dioattr.d_mem % sizeof (void *) || ! power_of_2(dioattr.d_mem)) {
371                 perror("d_mem bad");
372                 return 0;
373         }
374
375         /*
376          * Also make sure user doesn't specify a block size that's
377          * incompatible with the underlying file system.
378          */
379         if (! dioattr.d_miniosz) {
380                 perror("miniosz == 0!");
381                 return 0;
382         }
383         if (blocksize % dioattr.d_miniosz) {
384                 fprintf(stderr, "blocksize %d must be a multiple of "
385                         "%d for direct I/O\n", blocksize, dioattr.d_miniosz);
386                 return 0;
387         }
388
389         return (size_t) dioattr.d_mem;
390 }
391
392 static int
393 realtime_setup(char *filename, int fd)
394 {
395         struct fsxattr rtattr;
396
397         (void) memset(&rtattr, 0, sizeof rtattr);
398         if (xfscntl(filename, fd, FSGETXATTR, &rtattr) < 0) {
399                 perror("FSGETXATTR)");
400                 return 1;
401         }
402         if ((rtattr.fsx_xflags & XFS_XFLAG_REALTIME) == 0 ||
403             (extsize && rtattr.fsx_extsize != extsize * blocksize)) {
404                 rtattr.fsx_xflags |= XFS_XFLAG_REALTIME;
405                 if (extsize)
406                         rtattr.fsx_extsize = extsize * blocksize;
407                 if (xfscntl(filename, fd, FSSETXATTR, &rtattr) < 0) {
408                         perror("FSSETXATTR)");
409                         return 1;
410                 }
411         }
412
413         return 0;
414 }
415
416 int
417 main(int argc, char *argv[])
418 {
419         char    *filename;
420         size_t  size;
421         int     oflags;
422         int     fd;
423         size_t  alignment;
424         int     errors;
425
426         filename = parseargs(argc, argv);
427         if (! filename)
428                 return 1;
429
430         /*
431          * Allocate a bitmap big enough to track the range of
432          * blocks we'll be dealing with.
433          */
434         size = (filesize / blocksize) / 8 + 1;
435         valid = malloc(size);
436         if ((valid = malloc(size)) == NULL) {
437                 perror("malloc");
438                 return 1;
439         }
440         memset(valid, 0, size);
441
442         /* Lots of arguments affect how we open the file */
443         oflags = test ? O_RDONLY : O_RDWR|O_CREAT;
444         oflags |= preserve ? 0 : O_TRUNC;
445         oflags |= wsync ? O_SYNC : 0;
446         oflags |= direct ? O_DIRECT : 0;
447
448         /*
449          * Open the file, write rand block in random places, read them all
450          * back to check for correctness, then close the file.
451          */
452         if ((fd = open(filename, oflags, 0666)) < 0) {
453                 perror("open");
454                 return 1;
455         }
456         if (rt && realtime_setup(filename, fd))
457                 return 1;
458         alignment = get_alignment(filename, fd);
459         if (! alignment)
460                 return 1;
461
462         printf("write%s\n", test ? " (skipped)" : "");
463         writeblks(filename, fd, alignment);
464
465         printf("readback\n");
466         errors = readblks(fd, alignment);
467
468         if (close(fd) < 0) {
469                 perror("close");
470                 return 1;
471         }
472         free(valid);
473
474         if (errors) {
475                 printf("randholes: %d errors found during readback\n", errors);
476                 return 2;
477         }
478
479         printf("randholes: ok\n");
480
481         return 0;
482 }
483