xfstests: randholes: only allocate write buffer when needed
[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 "global.h"
20
21 unsigned char   *valid; /* Bit-vector array showing which blocks have been written */
22 int             nvalid; /* number of bytes in valid array */
23 #define SETBIT(ARRAY, N)        ((ARRAY)[(N)/8] |= (1 << ((N)%8)))
24 #define BITVAL(ARRAY, N)        ((ARRAY)[(N)/8] & (1 << ((N)%8)))
25
26 __uint64_t filesize;
27 unsigned int blocksize;
28 int count;
29 int verbose;
30 int wsync;
31 int direct;
32 int alloconly;
33 int rt;
34 int extsize;
35 int preserve;
36 int test;
37 __uint64_t fileoffset;
38 struct dioattr diob;
39 struct fsxattr rtattr;
40
41 #define READ_XFER       10      /* block to read at a time when checking */
42
43 void usage(char *progname);
44 int findblock(void);
45 void writeblks(char *fname, int fd);
46 int readblks(int fd);
47 void dumpblock(int *buffer, __uint64_t offset, int blocksize);
48
49 void
50 usage(char *progname)
51 {
52         fprintf(stderr,
53                 "usage: %s [-l filesize] [-b blocksize] [-c count]"
54                 " [-o write offset] [-s seed] [-x extentsize]"
55                 " [-w] [-v] [-d] [-r] [-a] [-p] filename\n",
56                 progname);
57         exit(1);
58 }
59
60 int
61 main(int argc, char *argv[])
62 {
63         int seed, ch, fd, oflags;
64         char *filename = NULL;
65         int r;
66
67         filesize = ((__uint64_t)256)*1024*1024;
68         blocksize = 512;
69         count = filesize/blocksize;
70         verbose = 0;
71         wsync = 0;
72         seed = time(NULL);
73         test = 0;
74         while ((ch = getopt(argc, argv, "b:l:s:c:o:x:vwdrapt")) != EOF) {
75                 switch(ch) {
76                 case 'b':       blocksize  = atoi(optarg);      break;
77                 case 'l':       filesize   = strtoull(optarg, NULL, 16); break;
78                 case 's':       seed       = atoi(optarg);      break;
79                 case 'c':       count      = atoi(optarg);      break;
80                 case 'o':       fileoffset = strtoull(optarg, NULL, 16); break;
81                 case 'x':       extsize    = atoi(optarg);      break;
82                 case 'v':       verbose++;                      break;
83                 case 'w':       wsync++;                        break;
84                 case 'd':       direct++;                       break;
85                 case 'r':       rt++;                           break;
86                 case 'a':       alloconly++;                    break;
87                 case 'p':       preserve++;                     break;
88                 case 't':       test++; preserve++;             break;
89                 default:        usage(argv[0]);                 break;
90                 }
91         }
92         if (optind == argc-1)
93                 filename = argv[optind];
94         else
95                 usage(argv[0]);
96         if ((filesize % blocksize) != 0) {
97                 filesize -= filesize % blocksize;
98                 printf("filesize not a multiple of blocksize, reducing filesize to %llu\n",
99                        (unsigned long long)filesize);
100         }
101         if ((fileoffset % blocksize) != 0) {
102                 fileoffset -= fileoffset % blocksize;
103                 printf("fileoffset not a multiple of blocksize, reducing fileoffset to %llu\n",
104                        (unsigned long long)fileoffset);
105         }
106         if (count > (filesize/blocksize)) {
107                 count = (filesize/blocksize);
108                 printf("count of blocks written is too large, setting to %d\n",
109                               count);
110         } else if (count < 1) {
111                 count = 1;
112                 printf("count of blocks written is too small, setting to %d\n",
113                               count);
114         }
115         printf("randholes: Seed = %d (use \"-s %d\" to re-execute this test)\n", seed, seed);
116         srandom(seed);
117         
118         printf("randholes: blocksize=%d, filesize=%llu, seed=%d\n"
119                "randholes: count=%d, offset=%llu, extsize=%d\n",
120                 blocksize, (unsigned long long)filesize, seed,
121                count, (unsigned long long)fileoffset, extsize);
122         printf("randholes: verbose=%d, wsync=%d, direct=%d, rt=%d, alloconly=%d, preserve=%d, test=%d\n",
123                 verbose, wsync, direct, rt, alloconly, preserve, test);
124         
125         /*
126          * Open the file, write rand block in random places, read them all
127          * back to check for correctness, then close the file.
128          */
129         nvalid = (filesize / blocksize) / 8 + 1;
130         if ((valid = (unsigned char *)calloc(1, (unsigned)nvalid)) == NULL) {
131                 perror("malloc");
132                 return 1;
133         }
134         if (rt)
135                 direct++;
136         
137         oflags=test?(O_RDONLY):(O_RDWR | O_CREAT);
138         oflags |=   (preserve ? 0 : O_TRUNC) |
139                     (wsync ? O_SYNC : 0) |
140                     (direct ? O_DIRECT : 0);
141         
142         if ((fd = open(filename, oflags, 0666)) < 0) {
143                 perror("open");
144                 return 1;
145         }
146
147         if (rt) {
148 #ifdef XFS_IOC_FSGETXATTR
149                 if (xfsctl(filename, fd, XFS_IOC_FSGETXATTR, &rtattr) < 0) {
150                         perror("xfsctl(XFS_IOC_FSGETXATTR)");
151                         return 1;
152                 }
153                 if ((rtattr.fsx_xflags & XFS_XFLAG_REALTIME) == 0 ||
154                     (extsize && rtattr.fsx_extsize != extsize * blocksize)) {
155                         rtattr.fsx_xflags |= XFS_XFLAG_REALTIME;
156                         if (extsize)
157                                 rtattr.fsx_extsize = extsize * blocksize;
158                         if (xfsctl(filename, fd, XFS_IOC_FSSETXATTR, &rtattr) < 0) {
159                                 perror("xfsctl(XFS_IOC_FSSETXATTR)");
160                                 return 1;
161                         }
162                 }
163 #else
164 #ifdef F_FSGETXATTR
165                 if (fcntl(fd, F_FSGETXATTR, &rtattr) < 0) {
166                         perror("fcntl(F_FSGETXATTR)");
167                         return 1;
168                 }
169                 if ((rtattr.fsx_xflags & XFS_XFLAG_REALTIME) == 0 ||
170                     (extsize && rtattr.fsx_extsize != extsize * blocksize)) {
171                         rtattr.fsx_xflags |= XFS_XFLAG_REALTIME;
172                         if (extsize)
173                                 rtattr.fsx_extsize = extsize * blocksize;
174                         if (fcntl(fd, F_FSSETXATTR, &rtattr) < 0) {
175                                 perror("fcntl(F_FSSETXATTR)");
176                                 return 1;
177                         }
178                 }
179 #else
180 bozo!
181 #endif
182 #endif
183         }
184
185         if (direct) {
186 #ifdef XFS_IOC_DIOINFO
187                 if (xfsctl(filename, fd, XFS_IOC_DIOINFO, &diob) < 0) {
188                         perror("xfsctl(XFS_IOC_FIOINFO)");
189                         return 1;
190                 }
191 #else
192 #ifdef F_DIOINFO
193                 if (fcntl(fd, F_DIOINFO, &diob) < 0) {
194                         perror("fcntl(F_FIOINFO)");
195                         return 1;
196                 }
197 #else
198 bozo!
199 #endif
200 #endif
201                 if (blocksize % diob.d_miniosz) {
202                         fprintf(stderr,
203                                 "blocksize %d must be a multiple of %d for direct I/O\n",
204                                 blocksize,
205                                 diob.d_miniosz);
206                         return 1;
207                 }
208         }
209         printf(test?"write (skipped)\n":"write\n");
210         writeblks(filename, fd);
211         printf("readback\n");
212         r=readblks(fd);
213         if (close(fd) < 0) {
214                 perror("close");
215                 return 1;
216         }
217         free(valid);
218         
219         if (r) {
220             printf("randholes: %d errors found during readback\n", r);
221             return 2;
222         } else {
223             printf("randholes: ok\n");
224             return 0;
225         }
226 }
227
228 void
229 writeblks(char *fname, int fd)
230 {
231         __uint64_t offset;
232         char *buffer;
233         int block;
234         struct flock64 fl;
235
236         if (test)
237                 buffer = NULL;
238         else {
239                 if (direct)
240                         buffer = memalign(diob.d_mem, blocksize);
241                 else
242                         buffer = malloc(blocksize);
243                 if (buffer == NULL) {
244                         perror("malloc");
245                         exit(1);
246                 }
247                 memset(buffer, 0, blocksize);
248         }
249
250         do {
251                 if (verbose && ((count % 100) == 0)) {
252                         printf(".");
253                         fflush(stdout);
254                 }
255                 block = findblock();
256                 if (block < 0) {
257                     perror("findblock");
258                     exit(1);
259                 }
260
261                 offset = (__uint64_t) block * blocksize;
262                 if (alloconly) {
263                         if (test) continue;
264                         
265                         fl.l_start = fileoffset + offset;
266                         fl.l_len = blocksize;
267                         fl.l_whence = 0;
268
269 #ifdef XFS_IOC_RESVSP64
270                         if (xfsctl(fname, fd, XFS_IOC_RESVSP64, &fl) < 0) {
271                                 perror("xfsctl(XFS_IOC_RESVSP64)");
272                                 exit(1);
273                         }
274 #else
275 #ifdef F_RESVSP64
276                         if (fcntl(fd, F_RESVSP64, &fl) < 0) {
277                                 perror("fcntl(F_RESVSP64)");
278                                 exit(1);
279                         }
280 #else
281 bozo!
282 #endif
283 #endif
284                         continue;
285                 }
286                 SETBIT(valid, block);
287                 if (!test) {
288                         if (lseek64(fd, fileoffset + offset, SEEK_SET) < 0) {
289                                 perror("lseek");
290                                 exit(1);
291                         }
292                         /*
293                          * Before writing, record offset at the base
294                          * of the buffer and at offset 256 bytes
295                          * into it.  We'll verify this when we read
296                          * it back in again.
297                          */
298                         *(__uint64_t *) buffer = fileoffset + offset;
299                         *(__uint64_t *) (buffer + 256) = fileoffset + offset;
300
301                         if (write(fd, buffer, blocksize) < blocksize) {
302                                 perror("write");
303                                 exit(1);
304                         }
305                 }
306                 if (verbose > 1) {
307                         printf("%swriting data at offset=%llx\n",
308                                 test ? "NOT " : "",
309                                 (unsigned long long) (fileoffset + offset));
310                 }
311         } while (--count);
312
313         free(buffer);
314 }
315
316 int
317 readblks(int fd)
318 {
319         unsigned long offset;
320         char *buffer, *tmp;
321         unsigned int xfer, block, i;
322         int err=0;
323
324         if (alloconly)
325                 return 0;
326         xfer = READ_XFER*blocksize;
327         if (direct)
328                 buffer = memalign(diob.d_mem, xfer);
329         else
330                 buffer = malloc(xfer);
331         if (buffer == NULL) {
332                 perror("malloc");
333                 exit(1);
334         }
335         memset(buffer, 0, xfer);
336         if (verbose)
337                 printf("\n");
338
339         if (lseek64(fd, fileoffset, SEEK_SET) < 0) {
340                 perror("lseek");
341                 exit(1);
342         }
343         for (offset = 0, block = 0; offset < filesize; offset += xfer) {
344                 if ((i = read(fd, buffer, xfer) < xfer)) {
345                         if (i < 2)
346                                 break;
347                         perror("read");
348                         exit(1);
349                 }
350                 for (tmp = buffer, i = 0; i < READ_XFER; i++, block++, tmp += blocksize) {
351                         if (verbose && ((block % 100) == 0)) {
352                                 printf("+");
353                                 fflush(stdout);
354                         }
355                         if (BITVAL(valid, block) == 0) {
356                                 if ((*(__uint64_t *)tmp != 0LL) ||
357                                     (*(__uint64_t *)(tmp+256) != 0LL)) {
358                                         printf("mismatched data at offset=%llx, expected 0x%llx, got 0x%llx and 0x%llx\n",
359                                                (unsigned long long)fileoffset + block * blocksize,
360                                                0LL,
361                                                *(unsigned long long *)tmp,
362                                                *(unsigned long long *)(tmp+256));
363                                         err++;
364                                 }
365                         } else {
366                                 if ( (*(__uint64_t *)tmp !=
367                                       fileoffset + block * blocksize) ||
368                                      (*(__uint64_t *)(tmp+256) !=
369                                       fileoffset + block * blocksize) ) {
370                                         printf("mismatched data at offset=%llx, "
371                                                "expected 0x%llx, got 0x%llx and 0x%llx\n",
372                                                (unsigned long long)fileoffset + block * blocksize,
373                                                (unsigned long long)fileoffset + block * blocksize,
374                                                *(unsigned long long *)tmp,
375                                                *(unsigned long long *)(tmp+256));
376                                         err++;
377                                 }
378                         }
379                         if (verbose > 2) {
380                                 printf("block %d blocksize %d\n", block,
381                                        blocksize);
382                                 dumpblock((int *)tmp,
383                                           fileoffset + block * blocksize,
384                                           blocksize);
385                         }
386                 }
387         }
388         if (verbose)
389                 printf("\n");
390
391         free(buffer);
392         return err;
393 }
394
395 int
396 findblock(void)
397 {
398         int block, numblocks;
399
400         numblocks = filesize / blocksize;
401         block = random() % numblocks;
402         if (BITVAL(valid, block) == 0)
403                 return(block);
404
405         for (  ; BITVAL(valid, block) != 0; block++) {
406                 if (block == (numblocks-1))
407                         block = -1;
408         }
409         if (block == -1)
410                 printf("returning block -1\n");
411         return(block);
412 }
413
414 void
415 dumpblock(int *buffer, __uint64_t offset, int blocksize)
416 {
417         int     i;
418
419         for (i = 0; i < (blocksize / 16); i++) {
420                 printf("%llx: 0x%08x 0x%08x 0x%08x 0x%08x\n",
421                        (unsigned long long)offset, *buffer, *(buffer + 1), *(buffer + 2),
422                        *(buffer + 3));
423                 offset += 16;
424                 buffer += 4;
425         }
426 }