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