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