/*
- * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
+ * Copyright (c) 2000-2003, 2010 SGI
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Further, this software is distributed without any warranty that it is
- * free of the rightful claim of any third person regarding infringement
- * or the like. Any license provided herein, whether implied or
- * otherwise, applies only to this software file. Patent licenses, if
- * any, provided herein do not apply to combinations of this program with
- * other software, or any other product whatsoever.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston MA 02111-1307, USA.
- *
- * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
- * Mountain View, CA 94043, or:
- *
- * http://www.sgi.com
- *
- * For further information regarding this notice, see:
- *
- * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <inttypes.h>
+
#include "global.h"
-unsigned char *valid; /* Bit-vector array showing which blocks have been written */
-int nvalid; /* number of bytes in valid array */
+#define power_of_2(x) ((x) && !((x) & ((x) - 1)))
+#define DEFAULT_FILESIZE ((uint64_t) (256 * 1024 * 1024))
+#define DEFAULT_BLOCKSIZE 512
+
#define SETBIT(ARRAY, N) ((ARRAY)[(N)/8] |= (1 << ((N)%8)))
#define BITVAL(ARRAY, N) ((ARRAY)[(N)/8] & (1 << ((N)%8)))
-off64_t filesize;
-int blocksize;
-int count;
-int verbose;
-int wsync;
-int direct;
-int alloconly;
-int rt;
-int extsize;
-int preserve;
-int test;
-off64_t fileoffset;
-struct dioattr diob;
-struct fsxattr rtattr;
-
-#define READ_XFER 10 /* block to read at a time when checking */
-
-void usage(char *progname);
-int findblock(void);
-void writeblks(int fd);
-int readblks(int fd);
-void dumpblock(int *buffer, off64_t offset, int blocksize);
-
-void
+/* Bit-vector array showing which blocks have been written */
+static unsigned char *valid;
+
+static uint64_t filesize;
+static uint64_t fileoffset;
+
+static unsigned int blocksize;
+static int count;
+static int verbose;
+static int wsync;
+static int direct;
+static int alloconly;
+static int rt;
+static int extsize; /* used only for real-time */
+static int preserve;
+static int test;
+
+#define READ_XFER 256 /* blocks to read at a time when checking */
+
+/*
+ * Define xfscntl() to mask the difference between the Linux
+ * and the Irix fcntl() interfaces to XFS for user space. The
+ * "cmd" argument is just the last part of the command, e.g.
+ * pass FSGETXATTR in place of either XFS_IOC_FSGETXATTR (Linux)
+ * F_FSGETXATTR (Irix).
+ *
+ */
+# define xfscntl(filename, fd, cmd, arg) \
+ xfsctl((filename), (fd), XFS_IOC_ ## cmd, (arg))
+
+static void
usage(char *progname)
{
- fprintf(stderr, "usage: %s [-l filesize] [-b blocksize] [-c count] [-o write offset] [-s seed] [-x extentsize] [-w] [-v] [-d] [-r] [-a] [-p] filename\n",
- progname);
+ fprintf(stderr,
+ "usage: %s [-l filesize] [-b blocksize] [-c count]\n"
+ "\t\t[-o write_offset] [-s seed] [-r [-x extentsize]]\n"
+ "\t\t[-w] [-v] [-d] [-a] [-p] [-t] filename\n\n",
+ progname);
+ fprintf(stderr, "\tdefault filesize is %" PRIu64 " bytes\n",
+ DEFAULT_FILESIZE);
+ fprintf(stderr, "\tdefault blocksize is %u bytes\n",
+ DEFAULT_BLOCKSIZE);
+ fprintf(stderr, "\tdefault count is %d block-sized writes\n",
+ (int) (DEFAULT_FILESIZE / DEFAULT_BLOCKSIZE));
+ fprintf(stderr, "\tdefault write_offset is %" PRIu64 " bytes\n",
+ (uint64_t) 0);
exit(1);
}
-int
-main(int argc, char *argv[])
+/* Returns filename if successful or a null pointer if an error occurs */
+static char *
+parseargs(int argc, char *argv[])
{
- int seed, ch, fd, oflags;
- char *filename;
- int r;
+ int seed;
+ int ch;
- filesize = ((off64_t)256)*1024*1024;
- blocksize = 512;
- count = filesize/blocksize;
+ filesize = DEFAULT_FILESIZE;
+ blocksize = DEFAULT_BLOCKSIZE;
+ count = (int) filesize / blocksize;
verbose = 0;
wsync = 0;
seed = time(NULL);
- test = 0;
+ test = 0;
while ((ch = getopt(argc, argv, "b:l:s:c:o:x:vwdrapt")) != EOF) {
switch(ch) {
case 'b': blocksize = atoi(optarg); break;
- case 'l': filesize = strtoll(optarg, NULL, 16); break;
+ case 'l': filesize = strtoull(optarg, NULL, 16); break;
case 's': seed = atoi(optarg); break;
case 'c': count = atoi(optarg); break;
- case 'o': fileoffset = strtoll(optarg, NULL, 16); break;
+ case 'o': fileoffset = strtoull(optarg, NULL, 16); break;
case 'x': extsize = atoi(optarg); break;
case 'v': verbose++; break;
case 'w': wsync++; break;
case 'd': direct++; break;
- case 'r': rt++; break;
+ case 'r': rt++; direct++; break;
case 'a': alloconly++; break;
case 'p': preserve++; break;
- case 't': test++; preserve++; break;
+ case 't': test++; preserve++; break;
default: usage(argv[0]); break;
}
}
- if (optind == argc-1)
- filename = argv[optind];
- else
+ if (optind != argc - 1)
usage(argv[0]);
+
if ((filesize % blocksize) != 0) {
filesize -= filesize % blocksize;
- printf("filesize not a multiple of blocksize, reducing filesize to %lld\n",
- filesize);
+ printf("filesize not a multiple of blocksize, "
+ "reducing filesize to %llu\n",
+ (unsigned long long) filesize);
}
if ((fileoffset % blocksize) != 0) {
fileoffset -= fileoffset % blocksize;
- printf("fileoffset not a multiple of blocksize, reducing fileoffset to %lld\n",
- fileoffset);
+ printf("fileoffset not a multiple of blocksize, "
+ "reducing fileoffset to %llu\n",
+ (unsigned long long) fileoffset);
}
if (count > (filesize/blocksize)) {
count = (filesize/blocksize);
- printf("count of blocks written is too large, setting to %d\n",
- count);
+ printf("count of blocks written is too large, "
+ "setting to %d\n", count);
} else if (count < 1) {
count = 1;
- printf("count of blocks written is too small, setting to %d\n",
- count);
+ printf("count of blocks written is too small, "
+ "setting to %d\n", count);
}
- printf("randholes: Seed = %d (use \"-s %d\" to re-execute this test)\n", seed, seed);
+ printf("randholes: Seed = %d (use \"-s %d\" "
+ "to re-execute this test)\n", seed, seed);
srandom(seed);
-
- printf("randholes: blocksize=%d, filesize=%Ld, seed=%d\n"
- "randholes: count=%d, offset=%Ld, extsize=%d\n",
- blocksize, filesize, seed, count, fileoffset, extsize);
- printf("randholes: verbose=%d, wsync=%d, direct=%d, rt=%d, alloconly=%d, preserve=%d, test=%d\n",
- verbose, wsync, direct, rt, alloconly, preserve, test);
-
- /*
- * Open the file, write rand block in random places, read them all
- * back to check for correctness, then close the file.
- */
- nvalid = (filesize / blocksize) / 8 + 1;
- if ((valid = (unsigned char *)calloc(1, (unsigned)nvalid)) == NULL) {
- perror("malloc");
- return 1;
- }
- if (rt)
- direct++;
-
- oflags=test?(O_RDONLY):(O_RDWR | O_CREAT);
- oflags |= (preserve ? 0 : O_TRUNC) |
- (wsync ? O_SYNC : 0) |
- (direct ? O_DIRECT : 0);
-
- if ((fd = open(filename, oflags, 0666)) < 0) {
- perror("open");
- return 1;
- }
- if (rt) {
- if (ioctl(fd, XFS_IOC_FSGETXATTR, &rtattr) < 0) {
- perror("ioctl(XFS_IOC_FSGETXATTR)");
- return 1;
- }
- if ((rtattr.fsx_xflags & XFS_XFLAG_REALTIME) == 0 ||
- (extsize && rtattr.fsx_extsize != extsize * blocksize)) {
- rtattr.fsx_xflags |= XFS_XFLAG_REALTIME;
- if (extsize)
- rtattr.fsx_extsize = extsize * blocksize;
- if (ioctl(fd, XFS_IOC_FSSETXATTR, &rtattr) < 0) {
- perror("ioctl(XFS_IOC_FSSETXATTR)");
- return 1;
- }
- }
- }
- if (direct) {
- if (ioctl(fd, XFS_IOC_DIOINFO, &diob) < 0) {
- perror("ioctl(XFS_IOC_FIOINFO)");
- return 1;
- }
- if (blocksize % diob.d_miniosz) {
- fprintf(stderr, "blocksize %d must be a multiple of %d for direct I/O\n", blocksize, diob.d_miniosz);
- return 1;
+
+ printf("randholes: blocksize=%d, filesize=%llu, seed=%d\n"
+ "randholes: count=%d, offset=%llu, extsize=%d\n",
+ blocksize, (unsigned long long)filesize, seed,
+ count, (unsigned long long)fileoffset, extsize);
+ printf("randholes: verbose=%d, wsync=%d, direct=%d, "
+ "rt=%d, alloconly=%d, preserve=%d, test=%d\n",
+ verbose, wsync, direct ? 1 : 0, rt, alloconly, preserve, test);
+
+ /* Last argument is the file name. Return it. */
+
+ return argv[optind]; /* Success */
+}
+
+/*
+ * Determine the next random block number to which to write.
+ * If an already-written block is selected, choose the next
+ * unused higher-numbered block. Returns the block number,
+ * or -1 if we exhaust available blocks looking for an unused
+ * one.
+ */
+static int
+findblock(void)
+{
+ int block, numblocks;
+
+ numblocks = filesize / blocksize;
+ block = random() % numblocks;
+
+ while (BITVAL(valid, block)) {
+ if (++block == numblocks) {
+ printf("returning block -1\n");
+ return -1;
}
}
- printf(test?"write (skipped)\n":"write\n");
- writeblks(fd);
- printf("readback\n");
- r=readblks(fd);
- if (close(fd) < 0) {
- perror("close");
- return 1;
+ return block;
+}
+
+static void
+dumpblock(int *buffer, uint64_t offset, int blocksize)
+{
+ int i;
+
+ for (i = 0; i < (blocksize / 16); i++) {
+ printf("%llx: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ (unsigned long long) offset, *buffer, *(buffer + 1),
+ *(buffer + 2), *(buffer + 3));
+ offset += 16;
+ buffer += 4;
}
- free(valid);
-
- if (r) {
- printf("randholes: %d errors found during readback\n", r);
- return 2;
- } else {
- printf("randholes: ok\n");
- return 0;
- }
}
-void
-writeblks(int fd)
+static void
+writeblks(char *fname, int fd, size_t alignment)
{
- off64_t offset;
- char *buffer;
+ uint64_t offset;
+ char *buffer = NULL;
int block;
+ int ret;
struct flock64 fl;
- if (direct)
- buffer = memalign(diob.d_mem, blocksize);
- else
- buffer = malloc(blocksize);
- if (buffer == NULL) {
- perror("malloc");
- exit(1);
+ if (!test) {
+ ret = posix_memalign((void **) &buffer, alignment, blocksize);
+ if (ret) {
+ fprintf(stderr, "posix_memalign: %s\n", strerror(ret));
+ exit(1);
+ }
+ memset(buffer, 0, blocksize);
+ }
+
+ /*
+ * Avoid allocation patterns being perturbed by different speculative
+ * preallocation beyond EOF configurations by first truncating the file
+ * to the expected maximum file size.
+ */
+ if (ftruncate(fd, filesize) < 0) {
+ perror("ftruncate");
+ exit(EXIT_FAILURE);
}
- memset(buffer, 0, blocksize);
- for ( ; count > 0; count--) {
+ do {
if (verbose && ((count % 100) == 0)) {
printf(".");
fflush(stdout);
}
block = findblock();
- offset = (off64_t)block * blocksize;
+ if (block < 0) {
+ perror("findblock");
+ exit(1);
+ }
+
+ offset = (uint64_t) block * blocksize;
if (alloconly) {
if (test) continue;
- fl.l_start = offset;
+ fl.l_start = fileoffset + offset;
fl.l_len = blocksize;
fl.l_whence = 0;
- if (ioctl(fd, XFS_IOC_RESVSP64, &fl) < 0) {
- perror("ioctl(XFS_IOC_RESVSP64)");
+
+ if (xfscntl(fname, fd, RESVSP64, &fl) < 0) {
+ perror("xfsnctl(RESVSP64)");
exit(1);
}
continue;
perror("lseek");
exit(1);
}
- }
- *(off64_t *)buffer = *(off64_t *)(buffer+256) =
- fileoffset + offset;
- if (!test) {
+ /*
+ * Before writing, record offset at the base
+ * of the buffer and at offset 256 bytes
+ * into it. We'll verify this when we read
+ * it back in again.
+ */
+ *(uint64_t *) buffer = fileoffset + offset;
+ *(uint64_t *) (buffer + 256) = fileoffset + offset;
+
if (write(fd, buffer, blocksize) < blocksize) {
perror("write");
exit(1);
}
}
- if (test && verbose>1) printf("NOT ");
if (verbose > 1) {
- printf("writing data at offset=%llx, value 0x%llx and 0x%llx\n",
- fileoffset + offset,
- *(off64_t *)buffer, *(off64_t *)(buffer+256));
+ printf("%swriting data at offset=%llx\n",
+ test ? "NOT " : "",
+ (unsigned long long) (fileoffset + offset));
}
- }
+ } while (--count);
free(buffer);
}
-int
-readblks(int fd)
+static int
+readblks(int fd, size_t alignment)
{
- long offset;
+ uint64_t offset;
char *buffer, *tmp;
- int xfer, block, i;
+ unsigned int xfer, block, i;
int err=0;
if (alloconly)
return 0;
xfer = READ_XFER*blocksize;
- if (direct)
- buffer = memalign(diob.d_mem, xfer);
- else
- buffer = malloc(xfer);
- if (buffer == NULL) {
- perror("malloc");
+ err = posix_memalign((void **) &buffer, alignment, xfer);
+ if (err) {
+ fprintf(stderr, "posix_memalign: %s\n", strerror(err));
exit(1);
}
memset(buffer, 0, xfer);
perror("lseek");
exit(1);
}
- for (offset = 0, block = 0; offset < filesize; offset += xfer) {
+ block = 0;
+ offset = 0;
+ while (offset < filesize) {
if ((i = read(fd, buffer, xfer) < xfer)) {
if (i < 2)
break;
perror("read");
exit(1);
}
- for (tmp = buffer, i = 0; i < READ_XFER; i++, block++, tmp += blocksize) {
+ tmp = buffer;
+ for (i = 0; i < READ_XFER; i++) {
+ uint64_t want;
+ uint64_t first;
+ uint64_t second;
+
if (verbose && ((block % 100) == 0)) {
printf("+");
fflush(stdout);
}
- if (BITVAL(valid, block) == 0) {
- if ((*(off64_t *)tmp != 0LL) ||
- (*(off64_t *)(tmp+256) != 0LL)) {
- printf("mismatched data at offset=%llx, expected 0x%llx, got 0x%llx and 0x%llx\n",
- fileoffset + block * blocksize,
- 0LL,
- *(off64_t *)tmp,
- *(off64_t *)(tmp+256));
- err++;
- }
- } else {
- if ( (*(off64_t *)tmp !=
- fileoffset + block * blocksize) ||
- (*(off64_t *)(tmp+256) !=
- fileoffset + block * blocksize) ) {
- printf("mismatched data at offset=%llx, expected 0x%llx, got 0x%llx and 0x%llx\n",
- fileoffset + block * blocksize,
- fileoffset + block * blocksize,
- *(off64_t *)tmp,
- *(off64_t *)(tmp+256));
- err++;
- }
+
+ want = BITVAL(valid, block) ? offset : 0;
+ first = *(uint64_t *) tmp;
+ second = *(uint64_t *) (tmp + 256);
+ if (first != want || second != want) {
+ printf("mismatched data at offset=0x%" PRIx64
+ ", expected 0x%" PRIx64
+ ", got 0x%" PRIx64
+ " and 0x%" PRIx64 "\n",
+ fileoffset + offset, want,
+ first, second);
+ err++;
}
if (verbose > 2) {
printf("block %d blocksize %d\n", block,
blocksize);
- dumpblock((int *)tmp,
- fileoffset + block * blocksize,
+ dumpblock((int *)tmp, fileoffset + offset,
blocksize);
}
+
+ block++;
+ offset += blocksize;
+ tmp += blocksize;
}
}
if (verbose)
return err;
}
-int
-findblock(void)
+/*
+ * Determine the memory alignment required for I/O buffers. For
+ * direct I/O we request the needed information from the file
+ * system; otherwise pointer alignment is fine. Returns the
+ * alignment multiple, or 0 if an error occurs.
+ */
+static size_t
+get_alignment(char *filename, int fd)
{
- int block, numblocks;
+ struct dioattr dioattr;
- numblocks = filesize / blocksize;
- block = random() % numblocks;
- if (BITVAL(valid, block) == 0)
- return(block);
+ if (! direct)
+ return sizeof (void *);
- for ( ; BITVAL(valid, block) != 0; block++) {
- if (block == (numblocks-1))
- block = -1;
+ memset(&dioattr, 0, sizeof dioattr);
+ if (xfscntl(filename, fd, DIOINFO, &dioattr) < 0) {
+ perror("xfscntl(FIOINFO)");
+ return 0;
}
- if (block == -1)
- printf("returning block -1\n");
- return(block);
+
+ /* Make sure the alignment meets the needs of posix_memalign() */
+
+ if (dioattr.d_mem % sizeof (void *) || ! power_of_2(dioattr.d_mem)) {
+ perror("d_mem bad");
+ return 0;
+ }
+
+ /*
+ * Also make sure user doesn't specify a block size that's
+ * incompatible with the underlying file system.
+ */
+ if (! dioattr.d_miniosz) {
+ perror("miniosz == 0!");
+ return 0;
+ }
+ if (blocksize % dioattr.d_miniosz) {
+ fprintf(stderr, "blocksize %d must be a multiple of "
+ "%d for direct I/O\n", blocksize, dioattr.d_miniosz);
+ return 0;
+ }
+
+ return (size_t) dioattr.d_mem;
}
-void
-dumpblock(int *buffer, off64_t offset, int blocksize)
+static int
+realtime_setup(char *filename, int fd)
{
- int i;
+ struct fsxattr rtattr;
- for (i = 0; i < (blocksize / 16); i++) {
- printf("%llx: 0x%08x 0x%08x 0x%08x 0x%08x\n",
- offset, *buffer, *(buffer + 1), *(buffer + 2),
- *(buffer + 3));
- offset += 16;
- buffer += 4;
+ (void) memset(&rtattr, 0, sizeof rtattr);
+ if (xfscntl(filename, fd, FSGETXATTR, &rtattr) < 0) {
+ perror("FSGETXATTR)");
+ return 1;
+ }
+ if ((rtattr.fsx_xflags & XFS_XFLAG_REALTIME) == 0 ||
+ (extsize && rtattr.fsx_extsize != extsize * blocksize)) {
+ rtattr.fsx_xflags |= XFS_XFLAG_REALTIME;
+ if (extsize)
+ rtattr.fsx_extsize = extsize * blocksize;
+ if (xfscntl(filename, fd, FSSETXATTR, &rtattr) < 0) {
+ perror("FSSETXATTR)");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *filename;
+ size_t size;
+ int oflags;
+ int fd;
+ size_t alignment;
+ int errors;
+
+ filename = parseargs(argc, argv);
+ if (! filename)
+ return 1;
+
+ /*
+ * Allocate a bitmap big enough to track the range of
+ * blocks we'll be dealing with.
+ */
+ size = (filesize / blocksize) / 8 + 1;
+ valid = malloc(size);
+ if ((valid = malloc(size)) == NULL) {
+ perror("malloc");
+ return 1;
}
+ memset(valid, 0, size);
+
+ /* Lots of arguments affect how we open the file */
+ oflags = test ? O_RDONLY : O_RDWR|O_CREAT;
+ oflags |= preserve ? 0 : O_TRUNC;
+ oflags |= wsync ? O_SYNC : 0;
+ oflags |= direct ? O_DIRECT : 0;
+
+ /*
+ * Open the file, write rand block in random places, read them all
+ * back to check for correctness, then close the file.
+ */
+ if ((fd = open(filename, oflags, 0666)) < 0) {
+ perror("open");
+ return 1;
+ }
+ if (rt && realtime_setup(filename, fd))
+ return 1;
+ alignment = get_alignment(filename, fd);
+ if (! alignment)
+ return 1;
+
+ printf("write%s\n", test ? " (skipped)" : "");
+ writeblks(filename, fd, alignment);
+
+ printf("readback\n");
+ errors = readblks(fd, alignment);
+
+ if (close(fd) < 0) {
+ perror("close");
+ return 1;
+ }
+ free(valid);
+
+ if (errors) {
+ printf("randholes: %d errors found during readback\n", errors);
+ return 2;
+ }
+
+ printf("randholes: ok\n");
+
+ return 0;
}
+