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