xfstests: randholes: encapsulate direct I/O setup code
[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 DEFAULT_FILESIZE        ((__uint64_t) (256 * 1024 * 1024))
29 #define DEFAULT_BLOCKSIZE       512
30
31 __uint64_t filesize;
32
33 unsigned int blocksize;
34 int count;
35 int verbose;
36 int wsync;
37 int direct;
38 int alloconly;
39 int rt;
40 int extsize;    /* used only for real-time */
41 int preserve;
42 int test;
43 __uint64_t fileoffset;
44 struct dioattr diob;
45 struct fsxattr rtattr;
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)
119 {
120         __uint64_t offset;
121         char *buffer;
122         int block;
123         struct flock64 fl;
124
125         if (test)
126                 buffer = NULL;
127         else {
128                 if (direct)
129                         buffer = memalign(diob.d_mem, blocksize);
130                 else
131                         buffer = malloc(blocksize);
132                 if (buffer == NULL) {
133                         perror("malloc");
134                         exit(1);
135                 }
136                 memset(buffer, 0, blocksize);
137         }
138
139         do {
140                 if (verbose && ((count % 100) == 0)) {
141                         printf(".");
142                         fflush(stdout);
143                 }
144                 block = findblock();
145                 if (block < 0) {
146                     perror("findblock");
147                     exit(1);
148                 }
149
150                 offset = (__uint64_t) block * blocksize;
151                 if (alloconly) {
152                         if (test) continue;
153                         
154                         fl.l_start = fileoffset + offset;
155                         fl.l_len = blocksize;
156                         fl.l_whence = 0;
157
158                         if (xfscntl(fname, fd, RESVSP64, &fl) < 0) {
159                                 perror("xfsnctl(RESVSP64)");
160                                 exit(1);
161                         }
162                         continue;
163                 }
164                 SETBIT(valid, block);
165                 if (!test) {
166                         if (lseek64(fd, fileoffset + offset, SEEK_SET) < 0) {
167                                 perror("lseek");
168                                 exit(1);
169                         }
170                         /*
171                          * Before writing, record offset at the base
172                          * of the buffer and at offset 256 bytes
173                          * into it.  We'll verify this when we read
174                          * it back in again.
175                          */
176                         *(__uint64_t *) buffer = fileoffset + offset;
177                         *(__uint64_t *) (buffer + 256) = fileoffset + offset;
178
179                         if (write(fd, buffer, blocksize) < blocksize) {
180                                 perror("write");
181                                 exit(1);
182                         }
183                 }
184                 if (verbose > 1) {
185                         printf("%swriting data at offset=%llx\n",
186                                 test ? "NOT " : "",
187                                 (unsigned long long) (fileoffset + offset));
188                 }
189         } while (--count);
190
191         free(buffer);
192 }
193
194 int
195 readblks(int fd)
196 {
197         unsigned long offset;
198         char *buffer, *tmp;
199         unsigned int xfer, block, i;
200         int err=0;
201
202         if (alloconly)
203                 return 0;
204         xfer = READ_XFER*blocksize;
205         if (direct)
206                 buffer = memalign(diob.d_mem, xfer);
207         else
208                 buffer = malloc(xfer);
209         if (buffer == NULL) {
210                 perror("malloc");
211                 exit(1);
212         }
213         memset(buffer, 0, xfer);
214         if (verbose)
215                 printf("\n");
216
217         if (lseek64(fd, fileoffset, SEEK_SET) < 0) {
218                 perror("lseek");
219                 exit(1);
220         }
221         for (offset = 0, block = 0; offset < filesize; offset += xfer) {
222                 if ((i = read(fd, buffer, xfer) < xfer)) {
223                         if (i < 2)
224                                 break;
225                         perror("read");
226                         exit(1);
227                 }
228                 for (tmp = buffer, i = 0; i < READ_XFER; i++, block++, tmp += blocksize) {
229                         if (verbose && ((block % 100) == 0)) {
230                                 printf("+");
231                                 fflush(stdout);
232                         }
233                         if (BITVAL(valid, block) == 0) {
234                                 if ((*(__uint64_t *)tmp != 0LL) ||
235                                     (*(__uint64_t *)(tmp+256) != 0LL)) {
236                                         printf("mismatched data at offset=%llx, expected 0x%llx, got 0x%llx and 0x%llx\n",
237                                                (unsigned long long)fileoffset + block * blocksize,
238                                                0LL,
239                                                *(unsigned long long *)tmp,
240                                                *(unsigned long long *)(tmp+256));
241                                         err++;
242                                 }
243                         } else {
244                                 if ( (*(__uint64_t *)tmp !=
245                                       fileoffset + block * blocksize) ||
246                                      (*(__uint64_t *)(tmp+256) !=
247                                       fileoffset + block * blocksize) ) {
248                                         printf("mismatched data at offset=%llx, "
249                                                "expected 0x%llx, got 0x%llx and 0x%llx\n",
250                                                (unsigned long long)fileoffset + block * blocksize,
251                                                (unsigned long long)fileoffset + block * blocksize,
252                                                *(unsigned long long *)tmp,
253                                                *(unsigned long long *)(tmp+256));
254                                         err++;
255                                 }
256                         }
257                         if (verbose > 2) {
258                                 printf("block %d blocksize %d\n", block,
259                                        blocksize);
260                                 dumpblock((int *)tmp,
261                                           fileoffset + block * blocksize,
262                                           blocksize);
263                         }
264                 }
265         }
266         if (verbose)
267                 printf("\n");
268
269         free(buffer);
270         return err;
271 }
272
273 int
274 direct_setup(char *filename, int fd)
275 {
276         if (xfscntl(filename, fd, DIOINFO, &diob) < 0) {
277                 perror("xfscntl(FIOINFO)");
278                 return 1;
279         }
280         if (blocksize % diob.d_miniosz) {
281                 fprintf(stderr, "blocksize %d must be a multiple of "
282                         "%d for direct I/O\n", blocksize, diob.d_miniosz);
283                 return 1;
284         }
285
286         return 0;
287 }
288
289 int
290 main(int argc, char *argv[])
291 {
292         int seed, ch, fd, oflags;
293         char *filename = NULL;
294         int r;
295
296         filesize = DEFAULT_FILESIZE;
297         blocksize = DEFAULT_BLOCKSIZE;
298         count = (int) filesize / blocksize;
299         verbose = 0;
300         wsync = 0;
301         seed = time(NULL);
302         test = 0;
303         while ((ch = getopt(argc, argv, "b:l:s:c:o:x:vwdrapt")) != EOF) {
304                 switch(ch) {
305                 case 'b':       blocksize  = atoi(optarg);      break;
306                 case 'l':       filesize   = strtoull(optarg, NULL, 16); break;
307                 case 's':       seed       = atoi(optarg);      break;
308                 case 'c':       count      = atoi(optarg);      break;
309                 case 'o':       fileoffset = strtoull(optarg, NULL, 16); break;
310                 case 'x':       extsize    = atoi(optarg);      break;
311                 case 'v':       verbose++;                      break;
312                 case 'w':       wsync++;                        break;
313                 case 'd':       direct++;                       break;
314                 case 'r':       rt++;                           break;
315                 case 'a':       alloconly++;                    break;
316                 case 'p':       preserve++;                     break;
317                 case 't':       test++; preserve++;             break;
318                 default:        usage(argv[0]);                 break;
319                 }
320         }
321         if (optind == argc-1)
322                 filename = argv[optind];
323         else
324                 usage(argv[0]);
325         if ((filesize % blocksize) != 0) {
326                 filesize -= filesize % blocksize;
327                 printf("filesize not a multiple of blocksize, reducing filesize to %llu\n",
328                        (unsigned long long)filesize);
329         }
330         if ((fileoffset % blocksize) != 0) {
331                 fileoffset -= fileoffset % blocksize;
332                 printf("fileoffset not a multiple of blocksize, reducing fileoffset to %llu\n",
333                        (unsigned long long)fileoffset);
334         }
335         if (count > (filesize/blocksize)) {
336                 count = (filesize/blocksize);
337                 printf("count of blocks written is too large, setting to %d\n",
338                               count);
339         } else if (count < 1) {
340                 count = 1;
341                 printf("count of blocks written is too small, setting to %d\n",
342                               count);
343         }
344         printf("randholes: Seed = %d (use \"-s %d\" to re-execute this test)\n", seed, seed);
345         srandom(seed);
346
347         printf("randholes: blocksize=%d, filesize=%llu, seed=%d\n"
348                "randholes: count=%d, offset=%llu, extsize=%d\n",
349                 blocksize, (unsigned long long)filesize, seed,
350                count, (unsigned long long)fileoffset, extsize);
351         printf("randholes: verbose=%d, wsync=%d, direct=%d, rt=%d, alloconly=%d, preserve=%d, test=%d\n",
352                 verbose, wsync, direct, rt, alloconly, preserve, test);
353
354         /*
355          * Open the file, write rand block in random places, read them all
356          * back to check for correctness, then close the file.
357          */
358         nvalid = (filesize / blocksize) / 8 + 1;
359         if ((valid = (unsigned char *)calloc(1, (unsigned)nvalid)) == NULL) {
360                 perror("malloc");
361                 return 1;
362         }
363         if (rt)
364                 direct++;
365
366         oflags=test?(O_RDONLY):(O_RDWR | O_CREAT);
367         oflags |=   (preserve ? 0 : O_TRUNC) |
368                     (wsync ? O_SYNC : 0) |
369                     (direct ? O_DIRECT : 0);
370
371         if ((fd = open(filename, oflags, 0666)) < 0) {
372                 perror("open");
373                 return 1;
374         }
375
376         if (rt) {
377                 if (xfscntl(filename, fd, FSGETXATTR, &rtattr) < 0) {
378                         perror("xfsnctl(FSGETXATTR)");
379                         return 1;
380                 }
381                 if ((rtattr.fsx_xflags & XFS_XFLAG_REALTIME) == 0 ||
382                     (extsize && rtattr.fsx_extsize != extsize * blocksize)) {
383                         rtattr.fsx_xflags |= XFS_XFLAG_REALTIME;
384                         if (extsize)
385                                 rtattr.fsx_extsize = extsize * blocksize;
386                         if (xfscntl(filename, fd, FSSETXATTR, &rtattr) < 0) {
387                                 perror("xfscntl(FSSETXATTR)");
388                                 return 1;
389                         }
390                 }
391         }
392
393         if (direct && direct_setup(filename, fd))
394                 return 1;
395
396         printf(test?"write (skipped)\n":"write\n");
397         writeblks(filename, fd);
398         printf("readback\n");
399         r=readblks(fd);
400         if (close(fd) < 0) {
401                 perror("close");
402                 return 1;
403         }
404         free(valid);
405
406         if (r) {
407             printf("randholes: %d errors found during readback\n", r);
408             return 2;
409         } else {
410             printf("randholes: ok\n");
411             return 0;
412         }
413 }