4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.gnu.org/licenses/gpl-2.0.html
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Use is subject to license terms.
26 * Copyright (c) 2011, Intel Corporation.
30 * This file is part of Lustre, http://www.lustre.org/
31 * Lustre is a trademark of Sun Microsystems, Inc.
33 * lustre/utils/llverdev.c
35 * Large Block Device Verification Tool.
36 * This program is used to test whether the block device is correctly
37 * handling IO beyond 2TB boundary.
38 * This tool have two working modes
42 * In full mode, the program writes a test pattern on the entire disk.
43 * The test pattern (device offset and timestamp) is written at the
44 * beginning of each 4kB block. When the whole device is full the read
45 * operation is performed to verify that the test pattern is correct.
47 * In partial mode, the program writes data at the critical locations
48 * of the device such as start of the device, before and after multiple of 1GB
49 * offset and at the end.
51 * A chunk buffer with default size of 1MB is used to write and read test
61 #ifndef _LARGEFILE64_SOURCE
62 #define _LARGEFILE64_SOURCE
64 #ifndef _FILE_OFFSET_BITS
65 #define _FILE_OFFSET_BITS 64
80 #include <sys/types.h>
82 #include <sys/ioctl.h>
83 #include <sys/mount.h>
85 #include <gnu/stubs.h>
87 #ifdef HAVE_EXT2FS_EXT2FS_H
88 # include <ext2fs/ext2fs.h>
91 #define ONE_MB (1024 * 1024)
92 #define ONE_GB (1024 * 1024 * 1024)
93 #define HALF_MB (ONE_MB / 2)
95 #define HALF_KB (ONE_KB / 2)
96 #define BLOCKSIZE 4096
98 /* Structure for writting test pattern */
100 unsigned long long bd_offset;
101 unsigned long long bd_time;
102 unsigned long long bd_inode;
104 static char *progname; /* name by which this program was run. */
105 static unsigned verbose = 1; /* prints offset in kB, operation rate */
106 static int readoption; /* run test in read-only (verify) mode */
107 static int writeoption; /* run test in write_only mode */
108 const char *devname; /* name of device to be tested. */
109 static unsigned full = 1; /* flag to full check */
110 static int error_count; /* number of IO errors hit during run */
111 static int isatty_flag;
113 static struct option const long_opts[] = {
114 { .val = 'c', .name = "chunksize", .has_arg = required_argument },
115 { .val = 'f', .name = "force", .has_arg = no_argument },
116 { .val = 'h', .name = "help", .has_arg = no_argument },
117 { .val = 'l', .name = "long", .has_arg = no_argument },
118 { .val = 'l', .name = "full", .has_arg = no_argument },
119 { .val = 'o', .name = "offset", .has_arg = required_argument },
120 { .val = 'p', .name = "partial", .has_arg = required_argument },
121 { .val = 'q', .name = "quiet", .has_arg = required_argument },
122 { .val = 'r', .name = "read", .has_arg = no_argument },
123 { .val = 't', .name = "timestamp", .has_arg = required_argument },
124 { .val = 'v', .name = "verbose", .has_arg = no_argument },
125 { .val = 'w', .name = "write", .has_arg = no_argument },
129 * Usage: displays help information, whenever user supply --help option in
130 * command or enters incorrect command line.
132 void usage(int status)
135 printf("\nUsage: %s [OPTION]... <device-name> ...\n",
137 printf("Block device verification tool.\n"
138 "\t-t {seconds}, --timestamp, "
139 "set test time (default=current time())\n"
140 "\t-o {offset}, --offset, "
141 "offset in kB of start of test, default=0\n"
142 "\t-r, --read, run in verify mode\n"
143 "\t-w, --write, run in test-pattern mode, default=rw\n"
146 "\t-l, --long, --full check of device\n"
147 "\t-p, --partial, for partial check (1GB steps)\n"
148 "\t-c {bytes}, --chunksize, IO size, default=1048576\n"
149 "\t-f, --force, force test to run without confirmation\n"
150 "\t-h, --help, display this help and exit\n");
156 * Open_dev: Opens device in specified mode and returns fd.
158 static int open_dev(const char *devname, int mode)
161 #ifdef HAVE_EXT2FS_EXT2FS_H
163 char mountpt[80] = "";
165 if (ext2fs_check_mount_point(devname, &mount_flags, mountpt,
167 fprintf(stderr, "%s: ext2fs_check_mount_point failed:%s",
168 progname, strerror(errno));
171 if (mount_flags & EXT2_MF_MOUNTED){
172 fprintf(stderr, "%s: %s is already mounted\n", progname,
177 fd = open(devname, mode | O_EXCL | O_LARGEFILE);
179 fprintf(stderr, "%s: Open failed: %s",progname,strerror(errno));
185 #ifdef HAVE_BLKID_BLKID_H
186 #include <blkid/blkid.h>
189 * sizeof_dev: Returns size of device in bytes
191 static loff_t sizeof_dev(int fd)
195 #ifdef HAVE_BLKID_BLKID_H
196 numbytes = blkid_get_dev_size(fd);
198 fprintf(stderr, "%s: blkid_get_dev_size(%s) failed",
204 # if defined BLKGETSIZE64 /* in sys/mount.h */
205 if (ioctl(fd, BLKGETSIZE64, &numbytes) >= 0)
208 # if defined BLKGETSIZE /* in sys/mount.h */
210 unsigned long sectors;
212 if (ioctl(fd, BLKGETSIZE, §ors) >= 0) {
213 numbytes = (loff_t)sectors << 9;
221 if (fstat(fd, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) {
222 numbytes = statbuf.st_size;
226 fprintf(stderr, "%s: unable to determine size of %s\n",
233 printf("%s: %s is %llu bytes (%g GB) in size\n",
235 (unsigned long long)numbytes, (double)numbytes / ONE_GB);
241 * Verify_chunk: Verifies test pattern in each 4kB (BLOCKSIZE) is correct.
242 * Returns 0 if test offset and timestamp is correct otherwise 1.
244 int verify_chunk(char *chunk_buf, const size_t chunksize,
245 unsigned long long chunk_off, const unsigned long long time_st,
246 const unsigned long long inode_st, const char *file)
248 struct block_data *bd;
251 for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
252 (char *)chunk_buf < chunk_end;
253 chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
254 bd = (struct block_data *)chunk_buf;
255 if ((bd->bd_offset == chunk_off) && (bd->bd_time == time_st) &&
256 (bd->bd_inode == inode_st))
259 fprintf(stderr, "\n%s: verify %s failed offset/timestamp/inode "
260 "%llu/%llu/%llu: found %llu/%llu/%llu instead\n",
261 progname, file, chunk_off, time_st, inode_st,
262 bd->bd_offset, bd->bd_time, bd->bd_inode);
270 * fill_chunk: Fills the chunk with current or user specified timestamp
271 * and offset. The test pattern is filled at the beginning of
272 * each 4kB(BLOCKSIZE) blocks in chunk_buf.
274 void fill_chunk(char *chunk_buf, size_t chunksize, loff_t chunk_off,
275 const time_t time_st, const ino_t inode_st)
277 struct block_data *bd;
280 for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
281 (char *)chunk_buf < chunk_end;
282 chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
283 bd = (struct block_data *)chunk_buf;
284 bd->bd_offset = chunk_off;
285 bd->bd_time = time_st;
286 bd->bd_inode = inode_st;
290 void show_rate(char *op, unsigned long long offset, unsigned long long *count)
303 printf("%s offset: %14llukB %5g MB/s ", op,
304 offset / ONE_KB, (double)(*count) /ONE_MB /diff);
317 * Write a chunk to disk, handling errors, interrupted writes, etc.
319 * If there is an IO error hit during the write, it is possible that
320 * this will just show up as a short write, and a subsequent write
321 * will return the actual error. We want to continue in the face of
322 * minor media errors so that we can validate the whole device if
323 * possible, but if there are many errors we don't want to loop forever.
325 * The error count will be returned upon exit to ensure that the
326 * media errors are detected even if nobody is looking at the output.
328 * Returns 0 on success, or -ve errno on failure.
330 size_t write_retry(int fd, const char *chunk_buf, size_t nrequested,
331 unsigned long long offset, const char *file)
336 nwritten = write(fd, chunk_buf, nrequested);
338 if (errno != ENOSPC) {
339 fprintf(stderr, "\n%s: write %s@%llu+%zi failed: %s\n",
340 progname, file, offset, nrequested,
342 if (error_count++ < 100)
347 if (nwritten < nrequested) {
348 fprintf(stderr, "\n%s: write %s@%llu+%zi short: %ld written\n",
349 progname, file, offset, nrequested, nwritten);
351 chunk_buf += nwritten;
352 nrequested -= nwritten;
360 * write_chunks: write the chunk_buf on the device. The number of write
361 * operations are based on the parameters write_end, offset, and chunksize.
363 * Returns 0 on success, or -ve error number on failure.
365 int write_chunks(int fd, unsigned long long offset,unsigned long long write_end,
366 char *chunk_buf, size_t chunksize, const time_t time_st,
367 const ino_t inode_st, const char *file)
369 unsigned long long stride, count = 0;
371 stride = full ? chunksize : (ONE_GB - chunksize);
372 for (offset = offset & ~(chunksize - 1); offset < write_end;
376 if (lseek64(fd, offset, SEEK_SET) == -1) {
377 fprintf(stderr, "\n%s: lseek64(%s+%llu) failed: %s\n",
378 progname, file, offset, strerror(errno));
381 if (offset + chunksize > write_end)
382 chunksize = write_end - offset;
383 if (!full && offset > chunksize) {
384 fill_chunk(chunk_buf, chunksize, offset, time_st,
386 ret = write_retry(fd, chunk_buf, chunksize,
392 if (offset + chunksize > write_end)
393 chunksize = write_end - offset;
395 fill_chunk(chunk_buf, chunksize, offset, time_st, inode_st);
396 ret = write_retry(fd, chunk_buf, chunksize, offset, file);
402 show_rate("write", offset, &count);
406 show_rate("write", offset, &count);
407 printf("\nwrite complete\n");
409 if (fsync(fd) == -1) {
410 fprintf(stderr, "%s: fsync failed: %s\n", progname,
418 * read_chunk: reads the chunk_buf from the device. The number of read
419 * operations are based on the parameters read_end, offset, and chunksize.
421 int read_chunks(int fd, unsigned long long offset, unsigned long long read_end,
422 char *chunk_buf, size_t chunksize, const time_t time_st,
423 const ino_t inode_st, const char *file)
425 unsigned long long stride, count = 0;
427 if (ioctl(fd, BLKFLSBUF, 0) < 0 && verbose)
428 fprintf(stderr, "%s: ioctl BLKFLSBUF failed: %s (ignoring)\n",
429 progname, strerror(errno));
431 stride = full ? chunksize : (ONE_GB - chunksize);
432 for (offset = offset & ~(chunksize - 1); offset < read_end;
436 if (lseek64(fd, offset, SEEK_SET) == -1) {
437 fprintf(stderr, "\n%s: lseek64(%llu) failed: %s\n",
438 progname, offset, strerror(errno));
441 if (offset + chunksize > read_end)
442 chunksize = read_end - offset;
444 if (!full && offset > chunksize) {
445 nread = read(fd, chunk_buf, chunksize);
447 fprintf(stderr,"\n%s: read %s@%llu+%zi failed: "
448 "%s\n", progname, file, offset,
449 chunksize, strerror(errno));
453 if (nread < chunksize) {
454 fprintf(stderr, "\n%s: read %s@%llu+%zi short: "
455 "%zi read\n", progname, file, offset,
459 if (verify_chunk(chunk_buf, nread, offset, time_st,
460 inode_st, file) != 0)
465 /* Need to reset position after read error */
466 if (nread < chunksize &&
467 lseek64(fd, offset, SEEK_SET) == -1) {
469 "\n%s: lseek64(%s@%llu) failed: %s\n",
470 progname, file, offset,strerror(errno));
473 if (offset + chunksize >= read_end)
474 chunksize = read_end - offset;
477 nread = read(fd, chunk_buf, chunksize);
479 fprintf(stderr, "\n%s: read failed: %s\n", progname,
484 if (nread < chunksize) {
485 fprintf(stderr, "\n%s: read %s@%llu+%zi short: "
486 "%zi read\n", progname, file, offset,
491 if (verify_chunk(chunk_buf, nread, offset, time_st,
492 inode_st, file) != 0)
497 show_rate("read", offset, &count);
500 show_rate("read", offset, &count);
501 printf("\nread complete\n");
506 int main(int argc, char **argv)
508 time_t time_st = 0; /* Default timestamp */
509 long long offset = 0, offset_orig; /* offset in kB */
510 size_t chunksize = ONE_MB; /* IO chunk size */
511 char *chunk_buf = NULL;
512 unsigned int force = 0; /* run test run without confirmation*/
513 unsigned long long dev_size = 0;
515 int mode = O_RDWR; /* mode which device should be opened */
519 progname = strrchr(argv[0], '/') == NULL ?
520 argv[0] : strrchr(argv[0], '/') + 1;
521 while ((c = getopt_long(argc, argv, "c:fhlo:pqrt:vw", long_opts,
525 chunksize = (strtoul(optarg, NULL, 0) * ONE_MB);
527 fprintf(stderr, "%s: chunk size value should be"
528 "nonzero and multiple of 1MB\n",
540 offset = strtoull(optarg, NULL, 0) * ONE_KB;
553 time_st = (time_t)strtoul(optarg, NULL, 0);
568 offset_orig = offset;
569 devname = argv[optind];
571 fprintf(stderr, "%s: device name not given\n", progname);
576 if (readoption && writeoption)
578 if (!readoption && !writeoption) {
583 if (!force && writeoption) {
584 printf("%s: permanently overwrite all data on %s (yes/no)? ",
586 if (scanf("%3s", yesno) == EOF && ferror(stdin)) {
587 perror("reading from stdin");
590 if (!(strcasecmp("yes", yesno) || strcasecmp("y", yesno))) {
591 printf("Not continuing due to '%s' response", yesno);
596 if (!writeoption && time_st == 0) {
597 fprintf(stderr, "%s: must give timestamp for read-only test\n",
602 fd = open_dev(devname, mode);
603 dev_size = sizeof_dev(fd);
605 fprintf(stderr, "%s: cannot test on device size < 1MB\n",
611 if (dev_size < (offset * 2)) {
612 fprintf(stderr, "%s: device size %llu < offset %llu\n",
613 progname, dev_size, offset);
618 (void)time(&time_st);
620 isatty_flag = isatty(STDOUT_FILENO);
623 printf("Timestamp: %lu\n", time_st);
625 chunk_buf = (char *)calloc(chunksize, 1);
626 if (chunk_buf == NULL) {
627 fprintf(stderr, "%s: memory allocation failed for chunk_buf\n",
633 c = write_chunks(fd, offset, dev_size, chunk_buf, chunksize,
634 time_st, 0, devname);
635 if (c < 0 && c != -ENOSPC) {
639 if (!full) { /* end of device aligned to a block */
640 offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
642 c = write_chunks(fd, offset, dev_size, chunk_buf,
643 chunksize, time_st, 0, devname);
644 if (c < 0 && c != -ENOSPC) {
649 offset = offset_orig;
652 if (read_chunks(fd, offset, dev_size, chunk_buf, chunksize,
653 time_st, 0, devname)) {
657 if (!full) { /* end of device aligned to a block */
658 offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
660 if (read_chunks(fd, offset, dev_size, chunk_buf,
661 chunksize, time_st, 0, devname)) {
667 printf("\n%s: data verified successfully\n", progname);