1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2000-2003, 2010 SGI
11 #define power_of_2(x) ((x) && !((x) & ((x) - 1)))
12 #define DEFAULT_FILESIZE ((uint64_t) (256 * 1024 * 1024))
13 #define DEFAULT_BLOCKSIZE 512
15 #define SETBIT(ARRAY, N) ((ARRAY)[(N)/8] |= (1 << ((N)%8)))
16 #define BITVAL(ARRAY, N) ((ARRAY)[(N)/8] & (1 << ((N)%8)))
18 /* Bit-vector array showing which blocks have been written */
19 static unsigned char *valid;
21 static uint64_t filesize;
22 static uint64_t fileoffset;
24 static unsigned int blocksize;
31 static int extsize; /* used only for real-time */
35 #define READ_XFER 256 /* blocks to read at a time when checking */
38 * Define xfscntl() to mask the difference between the Linux
39 * and the Irix fcntl() interfaces to XFS for user space. The
40 * "cmd" argument is just the last part of the command, e.g.
41 * pass FSGETXATTR in place of either XFS_IOC_FSGETXATTR (Linux)
42 * F_FSGETXATTR (Irix).
45 # define xfscntl(filename, fd, cmd, arg) \
46 xfsctl((filename), (fd), XFS_IOC_ ## cmd, (arg))
52 "usage: %s [-l filesize] [-b blocksize] [-c count]\n"
53 "\t\t[-o write_offset] [-s seed] [-r [-x extentsize]]\n"
54 "\t\t[-w] [-v] [-d] [-a] [-p] [-t] filename\n\n",
56 fprintf(stderr, "\tdefault filesize is %" PRIu64 " bytes\n",
58 fprintf(stderr, "\tdefault blocksize is %u bytes\n",
60 fprintf(stderr, "\tdefault count is %d block-sized writes\n",
61 (int) (DEFAULT_FILESIZE / DEFAULT_BLOCKSIZE));
62 fprintf(stderr, "\tdefault write_offset is %" PRIu64 " bytes\n",
67 /* Returns filename if successful or a null pointer if an error occurs */
69 parseargs(int argc, char *argv[])
74 filesize = DEFAULT_FILESIZE;
75 blocksize = DEFAULT_BLOCKSIZE;
76 count = (int) filesize / blocksize;
81 while ((ch = getopt(argc, argv, "b:l:s:c:o:x:vwdrapt")) != EOF) {
83 case 'b': blocksize = atoi(optarg); break;
84 case 'l': filesize = strtoull(optarg, NULL, 16); break;
85 case 's': seed = atoi(optarg); break;
86 case 'c': count = atoi(optarg); break;
87 case 'o': fileoffset = strtoull(optarg, NULL, 16); break;
88 case 'x': extsize = atoi(optarg); break;
89 case 'v': verbose++; break;
90 case 'w': wsync++; break;
91 case 'd': direct++; break;
92 case 'r': rt++; direct++; break;
93 case 'a': alloconly++; break;
94 case 'p': preserve++; break;
95 case 't': test++; preserve++; break;
96 default: usage(argv[0]); break;
99 if (optind != argc - 1)
102 if ((filesize % blocksize) != 0) {
103 filesize -= filesize % blocksize;
104 printf("filesize not a multiple of blocksize, "
105 "reducing filesize to %llu\n",
106 (unsigned long long) filesize);
108 if ((fileoffset % blocksize) != 0) {
109 fileoffset -= fileoffset % blocksize;
110 printf("fileoffset not a multiple of blocksize, "
111 "reducing fileoffset to %llu\n",
112 (unsigned long long) fileoffset);
114 if (count > (filesize/blocksize)) {
115 count = (filesize/blocksize);
116 printf("count of blocks written is too large, "
117 "setting to %d\n", count);
118 } else if (count < 1) {
120 printf("count of blocks written is too small, "
121 "setting to %d\n", count);
123 printf("randholes: Seed = %d (use \"-s %d\" "
124 "to re-execute this test)\n", seed, seed);
127 printf("randholes: blocksize=%d, filesize=%llu, seed=%d\n"
128 "randholes: count=%d, offset=%llu, extsize=%d\n",
129 blocksize, (unsigned long long)filesize, seed,
130 count, (unsigned long long)fileoffset, extsize);
131 printf("randholes: verbose=%d, wsync=%d, direct=%d, "
132 "rt=%d, alloconly=%d, preserve=%d, test=%d\n",
133 verbose, wsync, direct ? 1 : 0, rt, alloconly, preserve, test);
135 /* Last argument is the file name. Return it. */
137 return argv[optind]; /* Success */
141 * Determine the next random block number to which to write.
142 * If an already-written block is selected, choose the next
143 * unused higher-numbered block. Returns the block number,
144 * or -1 if we exhaust available blocks looking for an unused
150 int block, numblocks;
152 numblocks = filesize / blocksize;
153 block = random() % numblocks;
155 while (BITVAL(valid, block)) {
156 if (++block == numblocks) {
157 printf("returning block -1\n");
165 dumpblock(int *buffer, uint64_t offset, int blocksize)
169 for (i = 0; i < (blocksize / 16); i++) {
170 printf("%llx: 0x%08x 0x%08x 0x%08x 0x%08x\n",
171 (unsigned long long) offset, *buffer, *(buffer + 1),
172 *(buffer + 2), *(buffer + 3));
179 writeblks(char *fname, int fd, size_t alignment)
188 ret = posix_memalign((void **) &buffer, alignment, blocksize);
190 fprintf(stderr, "posix_memalign: %s\n", strerror(ret));
193 memset(buffer, 0, blocksize);
197 * Avoid allocation patterns being perturbed by different speculative
198 * preallocation beyond EOF configurations by first truncating the file
199 * to the expected maximum file size.
201 if (ftruncate(fd, filesize) < 0) {
207 if (verbose && ((count % 100) == 0)) {
217 offset = (uint64_t) block * blocksize;
221 fl.l_start = fileoffset + offset;
222 fl.l_len = blocksize;
225 if (xfscntl(fname, fd, RESVSP64, &fl) < 0) {
226 perror("xfsnctl(RESVSP64)");
231 SETBIT(valid, block);
233 if (lseek64(fd, fileoffset + offset, SEEK_SET) < 0) {
238 * Before writing, record offset at the base
239 * of the buffer and at offset 256 bytes
240 * into it. We'll verify this when we read
243 *(uint64_t *) buffer = fileoffset + offset;
244 *(uint64_t *) (buffer + 256) = fileoffset + offset;
246 if (write(fd, buffer, blocksize) < blocksize) {
252 printf("%swriting data at offset=%llx\n",
254 (unsigned long long) (fileoffset + offset));
262 readblks(int fd, size_t alignment)
266 unsigned int xfer, block, i;
271 xfer = READ_XFER*blocksize;
272 err = posix_memalign((void **) &buffer, alignment, xfer);
274 fprintf(stderr, "posix_memalign: %s\n", strerror(err));
277 memset(buffer, 0, xfer);
281 if (lseek64(fd, fileoffset, SEEK_SET) < 0) {
287 while (offset < filesize) {
288 if ((i = read(fd, buffer, xfer) < xfer)) {
295 for (i = 0; i < READ_XFER; i++) {
300 if (verbose && ((block % 100) == 0)) {
305 want = BITVAL(valid, block) ? offset : 0;
306 first = *(uint64_t *) tmp;
307 second = *(uint64_t *) (tmp + 256);
308 if (first != want || second != want) {
309 printf("mismatched data at offset=0x%" PRIx64
310 ", expected 0x%" PRIx64
312 " and 0x%" PRIx64 "\n",
313 fileoffset + offset, want,
318 printf("block %d blocksize %d\n", block,
320 dumpblock((int *)tmp, fileoffset + offset,
337 * Determine the memory alignment required for I/O buffers. For
338 * direct I/O we request the needed information from the file
339 * system; otherwise pointer alignment is fine. Returns the
340 * alignment multiple, or 0 if an error occurs.
343 get_alignment(char *filename, int fd)
345 struct dioattr dioattr;
348 return sizeof (void *);
350 memset(&dioattr, 0, sizeof dioattr);
351 if (xfscntl(filename, fd, DIOINFO, &dioattr) < 0) {
352 perror("xfscntl(FIOINFO)");
356 /* Make sure the alignment meets the needs of posix_memalign() */
358 if (dioattr.d_mem % sizeof (void *) || ! power_of_2(dioattr.d_mem)) {
364 * Also make sure user doesn't specify a block size that's
365 * incompatible with the underlying file system.
367 if (! dioattr.d_miniosz) {
368 perror("miniosz == 0!");
371 if (blocksize % dioattr.d_miniosz) {
372 fprintf(stderr, "blocksize %d must be a multiple of "
373 "%d for direct I/O\n", blocksize, dioattr.d_miniosz);
377 return (size_t) dioattr.d_mem;
381 realtime_setup(char *filename, int fd)
383 struct fsxattr rtattr;
385 (void) memset(&rtattr, 0, sizeof rtattr);
386 if (xfscntl(filename, fd, FSGETXATTR, &rtattr) < 0) {
387 perror("FSGETXATTR)");
390 if ((rtattr.fsx_xflags & XFS_XFLAG_REALTIME) == 0 ||
391 (extsize && rtattr.fsx_extsize != extsize * blocksize)) {
392 rtattr.fsx_xflags |= XFS_XFLAG_REALTIME;
394 rtattr.fsx_extsize = extsize * blocksize;
395 if (xfscntl(filename, fd, FSSETXATTR, &rtattr) < 0) {
396 perror("FSSETXATTR)");
405 main(int argc, char *argv[])
414 filename = parseargs(argc, argv);
419 * Allocate a bitmap big enough to track the range of
420 * blocks we'll be dealing with.
422 size = (filesize / blocksize) / 8 + 1;
423 valid = malloc(size);
424 if ((valid = malloc(size)) == NULL) {
428 memset(valid, 0, size);
430 /* Lots of arguments affect how we open the file */
431 oflags = test ? O_RDONLY : O_RDWR|O_CREAT;
432 oflags |= preserve ? 0 : O_TRUNC;
433 oflags |= wsync ? O_SYNC : 0;
434 oflags |= direct ? O_DIRECT : 0;
437 * Open the file, write rand block in random places, read them all
438 * back to check for correctness, then close the file.
440 if ((fd = open(filename, oflags, 0666)) < 0) {
444 if (rt && realtime_setup(filename, fd))
446 alignment = get_alignment(filename, fd);
450 printf("write%s\n", test ? " (skipped)" : "");
451 writeblks(filename, fd, alignment);
453 printf("readback\n");
454 errors = readblks(fd, alignment);
463 printf("randholes: %d errors found during readback\n", errors);
467 printf("randholes: ok\n");