2 * Large Block Device Verification Tool.
3 * This program is used to test whether the block device is correctly
4 * handling IO beyond 2TB boundary.
5 * This tool have two working modes
8 * The full mode is basic mode in which program writes the test pattern
9 * on entire disk. The test pattern (device offset and timestamp) is written
10 * at the beginning of each 4kB block. When the whole device is full then
11 * read operation is performed to verify that the test pattern is correct.
12 * In the fast mode the program writes data at the critical locations
13 * of the device such as start of the device, before and after multiple of 1GB
14 * offset and at the end.
15 * A chunk buffer with default size of 1MB is used to write and read test
25 #ifndef _LARGEFILE64_SOURCE
26 #define _LARGEFILE64_SOURCE
28 #ifndef _FILE_OFFSET_BITS
29 #define _FILE_OFFSET_BITS 64
44 #include <sys/types.h>
46 #include <sys/ioctl.h>
47 #include <sys/mount.h>
49 #include <gnu/stubs.h>
51 #ifdef HAVE_EXT2FS_EXT2FS_H
52 # include <ext2fs/ext2fs.h>
55 #define ONE_MB (1024 * 1024)
56 #define ONE_GB (1024 * 1024 * 1024)
57 #define HALF_MB (ONE_MB / 2)
59 #define HALF_KB (ONE_KB / 2)
60 #define BLOCKSIZE 4096
62 /* Structure for writting test pattern */
67 static char *progname; /* name by which this program was run. */
68 static unsigned verbose = 1; /* prints offset in kB, operation rate */
69 static int readoption; /* run test in read-only (verify) mode */
70 static int writeoption; /* run test in write_only mode */
71 const char *devname; /* name of device to be tested. */
72 static unsigned full = 1; /* flag to full check */
74 static int isatty_flag;
76 static struct option const longopts[] =
78 { "chunksize", required_argument, 0, 'c' },
79 { "force", no_argument, 0, 'f' },
80 { "help", no_argument, 0, 'h' },
81 { "offset", required_argument, 0, 'o' },
82 { "partial", required_argument, 0, 'p' },
83 { "quiet", required_argument, 0, 'q' },
84 { "read", no_argument, 0, 'r' },
85 { "timestamp", required_argument, 0, 't' },
86 { "verbose", no_argument, 0, 'v' },
87 { "write", no_argument, 0, 'w' },
88 { "long", no_argument, 0, 'l' },
93 * Usage: displays help information, whenever user supply --help option in
94 * command or enters incorrect command line.
96 void usage(int status)
99 printf("\nUsage: %s [OPTION]... <device-name> ...\n",
101 printf("Block device verification tool.\n"
102 "\t-t {seconds}, --timestamp, "
103 "set test time (default=current time())\n"
104 "\t-o {offset}, --offset, "
105 "offset in kB of start of test, default=0\n"
106 "\t-r, --read run test in verify mode\n"
107 "\t-w, --write run test in test-pattern mode, default=rw\n"
110 "\t-l, --long, full check of device\n"
111 "\t-p, --partial, for partial check (1GB steps)\n"
112 "\t-c, --chunksize, IO chunk size, default=1048576\n"
113 "\t-f, --force, force test to run without confirmation\n"
114 "\t-h, --help display this help and exit\n");
120 * Open_dev: Opens device in specified mode and returns fd.
122 static int open_dev(const char *devname, int mode)
124 #ifdef HAVE_EXT2FS_EXT2FS_H
126 char mountpt[80] = "";
128 if (ext2fs_check_mount_point(devname, &mount_flags, mountpt,
130 fprintf(stderr, "%s: ext2fs_check_mount_point failed:%s",
131 progname, strerror(errno));
134 if (mount_flags & EXT2_MF_MOUNTED){
135 fprintf(stderr, "%s: %s is already mounted\n", progname,
140 fd = open(devname, mode | O_EXCL | O_LARGEFILE);
142 fprintf(stderr, "%s: Open failed: %s",progname,strerror(errno));
148 #undef HAVE_BLKID_BLKID_H /* sigh, RHEL3 systems do not have libblkid.so.1 */
149 #ifdef HAVE_BLKID_BLKID_H
150 #include <blkid/blkid.h>
153 * sizeof_dev: Returns size of device in bytes
155 static loff_t sizeof_dev(int fd)
159 #ifdef HAVE_BLKID_BLKID_H
160 numbytes = blkid_get_dev_size(fd);
162 fprintf(stderr, "%s: blkid_get_dev_size(%s) failed",
168 # if defined BLKGETSIZE64 /* in sys/mount.h */
169 if (ioctl(fd, BLKGETSIZE64, &numbytes) >= 0)
172 # if defined BLKGETSIZE /* in sys/mount.h */
174 unsigned long sectors;
176 if (ioctl(fd, BLKGETSIZE, §ors) >= 0) {
177 numbytes = (loff_t)sectors << 9;
185 if (fstat(fd, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) {
186 numbytes = statbuf.st_size;
190 fprintf(stderr, "%s: unable to determine size of %s\n",
197 printf("%s: %s is %llu bytes (%g GB) in size\n",
199 (unsigned long long)numbytes, (double)numbytes / ONE_GB);
205 * Verify_chunk: Verifies test pattern in each 4kB (BLOCKSIZE) is correct.
206 * Returns 0 if test offset and timestamp is correct otherwise 1.
208 int verify_chunk(char *chunk_buf, size_t chunksize,
209 unsigned long long chunk_off, time_t time_st)
211 struct block_data *bd;
214 for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
215 (char *)chunk_buf < chunk_end;
216 chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
217 bd = (struct block_data *)chunk_buf;
218 if ((bd->bd_offset == chunk_off) && (bd->bd_time == time_st))
221 fprintf(stderr, "\n%s: verify failed at offset/timestamp "
222 "%llu/%lu: found %llu/%lu instead\n", progname,
223 chunk_off, time_st, bd->bd_offset, bd->bd_time);
230 * fill_chunk: Fills the chunk with current or user specified timestamp
231 * and offset. The test patters is filled at the beginning of
232 * each 4kB(BLOCKSIZE) blocks in chunk_buf.
234 void fill_chunk(char *chunk_buf, size_t chunksize, loff_t chunk_off,
237 struct block_data *bd;
240 for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
241 (char *)chunk_buf < chunk_end;
242 chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
243 bd = (struct block_data *)chunk_buf;
244 bd->bd_offset = chunk_off;
245 bd->bd_time = time_st;
249 void show_rate(char *op, unsigned long long offset, unsigned long long *count)
262 printf("%s offset: %14llukB %5g MB/s ", op,
263 offset / ONE_KB, (double)(*count) /ONE_MB /diff);
276 * write_chunk: write the chunk_buf on the device. The number of write
277 * operations are based on the parameters write_end, offset, and chunksize.
279 int write_chunks(unsigned long long offset, unsigned long long write_end,
280 char *chunk_buf, size_t chunksize, time_t time_st)
282 unsigned long long stride, count = 0;
284 stride = full ? chunksize : (ONE_GB - chunksize);
286 for (offset = offset & ~(chunksize - 1); offset < write_end;
288 if (lseek64(fd, offset, SEEK_SET) == -1) {
289 fprintf(stderr, "\n%s: lseek64(%llu) failed: %s\n",
290 progname, offset, strerror(errno));
293 if (offset + chunksize > write_end)
294 chunksize = write_end - offset;
296 if (!full && offset > chunksize) {
297 fill_chunk(chunk_buf, chunksize, offset, time_st);
298 if (write(fd, chunk_buf, chunksize) < 0) {
299 fprintf(stderr, "\n%s: write %llu failed: %s\n",
300 progname, offset, strerror(errno));
304 if (offset + chunksize > write_end)
305 chunksize = write_end - offset;
308 fill_chunk(chunk_buf, chunksize, offset, time_st);
309 if (write(fd, chunk_buf, chunksize) < 0) {
310 fprintf(stderr, "\n%s: write %llu failed: %s\n",
311 progname, offset, strerror(errno));
317 show_rate("write", offset, &count);
320 show_rate("write", offset, &count);
321 printf("\nwrite complete\n");
323 if (fsync(fd) == -1) {
324 fprintf(stderr, "%s: fsync faild: %s\n", progname,
332 * read_chunk: reads the chunk_buf from the device. The number of read
333 * operations are based on the parameters read_end, offset, and chunksize.
335 int read_chunks(unsigned long long offset, unsigned long long read_end,
336 char *chunk_buf, size_t chunksize, time_t time_st)
338 unsigned long long stride, count = 0;
340 stride = full ? chunksize : (ONE_GB - chunksize);
342 if (ioctl(fd, BLKFLSBUF, 0) < 0 && verbose)
343 fprintf(stderr, "%s: ioctl BLKFLSBUF failed: %s (ignoring)\n",
344 progname, strerror(errno));
346 for (offset = offset & ~(chunksize - 1); offset < read_end;
348 if (lseek64(fd, offset, SEEK_SET) == -1) {
349 fprintf(stderr, "\n%s: lseek64(%llu) failed: %s\n",
350 progname, offset, strerror(errno));
353 if (offset + chunksize > read_end)
354 chunksize = read_end - offset;
356 if (!full && offset > chunksize) {
357 if (read (fd, chunk_buf, chunksize) < 0) {
358 fprintf(stderr, "\n%s: read %llu failed: %s\n",
359 progname, offset, strerror(errno));
362 if (verify_chunk(chunk_buf, chunksize, offset,
366 if (offset + chunksize >= read_end)
367 chunksize = read_end - offset;
370 if (read(fd, chunk_buf, chunksize) < 0) {
371 fprintf(stderr, "\n%s: read failed: %s\n", progname,
376 if (verify_chunk(chunk_buf, chunksize, offset, time_st) != 0)
381 show_rate("read", offset, &count);
384 show_rate("read", offset, &count);
385 printf("\nread complete\n");
390 int main(int argc, char **argv)
392 time_t time_st = 0; /* Default timestamp */
393 long long offset = 0, offset_orig; /* offset in kB */
394 size_t chunksize = ONE_MB; /* IO chunk size */
395 char *chunk_buf = NULL;
396 unsigned int force = 0; /* run test run without confirmation*/
397 unsigned long long dev_size = 0;
399 int mode = O_RDWR; /* mode which device should be opened */
402 progname = strrchr(argv[0], '/') == NULL ?
403 argv[0] : strrchr(argv[0], '/') + 1;
404 while ((c = getopt_long(argc, argv, "c:fhlo:pqrt:vw", longopts,
408 chunksize = (strtoul(optarg, NULL, 0) * ONE_MB);
410 fprintf(stderr, "%s: chunk size value should be"
411 "nonzero and multiple of 1MB\n",
423 offset = strtoull(optarg, NULL, 0) * ONE_KB;
436 time_st = (time_t)strtoul(optarg, NULL, 0);
451 offset_orig = offset;
452 devname = argv[optind];
454 fprintf(stderr, "%s: device name not given\n", progname);
459 if (readoption && writeoption)
461 if (!readoption && !writeoption) {
466 if (!force && writeoption) {
467 printf("%s: permanently overwrite all data on %s (yes/no)? ",
470 if (!(strcasecmp("yes", yesno) || strcasecmp("y", yesno))) {
471 printf("Not continuing due to '%s' response", yesno);
476 if (!writeoption && time_st == 0) {
477 fprintf(stderr, "%s: must give timestamp for read-only test\n",
482 fd = open_dev(devname, mode);
483 dev_size = sizeof_dev(fd);
485 fprintf(stderr, "%s: cannot test on device size < 1MB\n",
491 if (dev_size < (offset * 2)) {
492 fprintf(stderr, "%s: device size %llu < offset %llu\n",
493 progname, dev_size, offset);
498 (void)time(&time_st);
500 isatty_flag = isatty(STDOUT_FILENO);
503 printf("Timestamp: %lu\n", time_st);
505 chunk_buf = (char *)calloc(chunksize, 1);
506 if (chunk_buf == NULL) {
507 fprintf(stderr, "%s: memory allocation failed for chunk_buf\n",
513 if (write_chunks(offset, dev_size, chunk_buf, chunksize,
518 if (!full) { /* end of device aligned to a block */
519 offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
521 if (write_chunks(offset, dev_size, chunk_buf, chunksize,
527 offset = offset_orig;
530 if (read_chunks(offset, dev_size, chunk_buf, chunksize,
535 if (!full) { /* end of device aligned to a block */
536 offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
538 if (read_chunks(offset, dev_size, chunk_buf, chunksize,
545 printf("\n%s: data verified successfully\n", progname);