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/
32 * lustre/utils/llverdev.c
34 * Large Block Device Verification Tool.
35 * This program is used to test whether the block device is correctly
36 * handling IO beyond 2TB boundary.
37 * This tool have two working modes
41 * In full mode, the program writes a test pattern on the entire disk.
42 * The test pattern (device offset and timestamp) is written at the
43 * beginning of each 4kB block. When the whole device is full the read
44 * operation is performed to verify that the test pattern is correct.
46 * In partial mode, the program writes data at the critical locations
47 * of the device such as start of the device, before and after multiple of 1GB
48 * offset and at the end.
50 * A chunk buffer with default size of 1MB is used to write and read test
60 #ifndef _LARGEFILE64_SOURCE
61 #define _LARGEFILE64_SOURCE
63 #ifndef _FILE_OFFSET_BITS
64 #define _FILE_OFFSET_BITS 64
79 #include <sys/types.h>
81 #include <sys/ioctl.h>
82 #include <sys/mount.h>
84 #include <gnu/stubs.h>
86 #define ONE_MB (1024 * 1024)
87 #define ONE_GB (1024 * 1024 * 1024)
88 #define HALF_MB (ONE_MB / 2)
90 #define HALF_KB (ONE_KB / 2)
91 #define BLOCKSIZE 4096
92 #define MAX_ALLOWED_IO_ERR 100
94 /* Structure for writting test pattern */
96 unsigned long long bd_offset;
97 unsigned long long bd_time;
98 unsigned long long bd_inode;
101 static char *progname; /* name by which this program was run. */
102 static unsigned int verbose = 1; /* prints offset in kB, operation rate */
103 static int readoption; /* run test in read-only (verify) mode */
104 static int writeoption; /* run test in write_only mode */
105 const char *devname; /* name of device to be tested. */
106 static unsigned int full = 1; /* flag to full check */
107 static int error_count; /* number of IO errors hit during run */
108 static ino_t ino_st; /* inode number of file under test */
109 static int isatty_flag;
111 static struct option const long_opts[] = {
112 { .val = 'c', .name = "chunksize", .has_arg = required_argument },
113 { .val = 'f', .name = "force", .has_arg = no_argument },
114 { .val = 'h', .name = "help", .has_arg = no_argument },
115 { .val = 'l', .name = "long", .has_arg = no_argument },
116 { .val = 'l', .name = "full", .has_arg = no_argument },
117 { .val = 'o', .name = "offset", .has_arg = required_argument },
118 { .val = 'p', .name = "partial", .has_arg = required_argument },
119 { .val = 'q', .name = "quiet", .has_arg = required_argument },
120 { .val = 'r', .name = "read", .has_arg = no_argument },
121 { .val = 's', .name = "size", .has_arg = required_argument },
122 { .val = 't', .name = "timestamp", .has_arg = required_argument },
123 { .val = 'v', .name = "verbose", .has_arg = no_argument },
124 { .val = 'w', .name = "write", .has_arg = no_argument },
128 * Usage: displays help information, whenever user supply --help option in
129 * command or enters incorrect command line.
131 static void usage(int status)
134 printf("\nUsage: %s [OPTION]... <device-name> ...\n",
136 printf("Block device verification tool.\n"
137 "\t-c|--chunksize chunk_mb, IO size in MB, default=1\n"
138 "\t-f|--force, force test to run without confirmation\n"
139 "\t-h|--help, display this help and exit\n"
140 "\t-l|--long, --full check of device\n"
141 "\t-o|--offset, offset in kB from start of file\n"
142 "\t-p|--partial, for partial check (1GB steps)\n"
144 "\t-r|--read, run in verify mode\n"
145 "\t-s|--size, file/device size in MB to read/write\n"
146 "\t-t|--timestamp seconds, set verification timestamp\n"
148 "\t-w|--write, run in test-pattern mode, default=rw\n"
155 * Open_dev: Opens device in specified mode and returns fd.
157 static int open_dev(const char *devname, int mode)
161 fd = open(devname, mode | O_EXCL | O_LARGEFILE);
163 fprintf(stderr, "%s: Open failed: %s\n",
164 progname, strerror(errno));
171 * sizeof_dev: Returns size of device in bytes
173 static size_t sizeof_dev(int fd, unsigned long long dev_size)
175 unsigned long long numbytes = 0;
178 if (fstat(fd, &statbuf) == 0) {
179 ino_st = statbuf.st_ino;
180 if (S_ISREG(statbuf.st_mode)) {
183 else if (statbuf.st_size > 1)
184 numbytes = statbuf.st_size;
186 numbytes = (~0ULL >> 1);
189 if (!S_ISBLK(statbuf.st_mode)) {
190 fprintf(stderr, "%s: '%s' not a block device or file\n",
195 fprintf(stderr, "%s: error accessing '%s': %s\n",
196 progname, devname, strerror(errno));
198 #if defined BLKGETSIZE64 /* in sys/mount.h */
199 if (ioctl(fd, BLKGETSIZE64, &numbytes) >= 0)
202 #if defined BLKGETSIZE /* in sys/mount.h */
204 unsigned long sectors;
206 if (ioctl(fd, BLKGETSIZE, §ors) >= 0) {
207 numbytes = (loff_t)sectors << 9;
212 fprintf(stderr, "%s: unable to determine size of %s\n",
216 if (dev_size && dev_size < numbytes)
222 * Verify_chunk: Verifies test pattern in each 4kB (BLOCKSIZE) is correct.
223 * Returns 0 if test offset and timestamp is correct otherwise 1.
225 static int verify_chunk(char *chunk_buf, const size_t chunksize,
226 unsigned long long chunk_off,
227 const unsigned long long time_st,
228 const unsigned long long inode_st,
231 struct block_data *bd;
234 for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
235 (char *)chunk_buf < chunk_end;
236 chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
237 bd = (struct block_data *)chunk_buf;
238 if ((bd->bd_offset == chunk_off) && (bd->bd_time == time_st) &&
239 (bd->bd_inode == inode_st))
243 "\n%s: verify %s failed offset/timestamp/inode %llu/%llu/%llu: found %llu/%llu/%llu instead\n",
244 progname, file, chunk_off, time_st, inode_st,
245 bd->bd_offset, bd->bd_time, bd->bd_inode);
253 * fill_chunk: Fills the chunk with current or user specified timestamp
254 * and offset. The test pattern is filled at the beginning of
255 * each 4kB(BLOCKSIZE) blocks in chunk_buf.
257 static void fill_chunk(char *chunk_buf, size_t chunksize, loff_t chunk_off,
258 const time_t time_st, const ino_t inode_st)
260 struct block_data *bd;
263 for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
264 (char *)chunk_buf < chunk_end;
265 chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
266 bd = (struct block_data *)chunk_buf;
267 bd->bd_offset = chunk_off;
268 bd->bd_time = time_st;
269 bd->bd_inode = inode_st;
273 static void show_rate(char *op, unsigned long long offset,
274 unsigned long long count)
276 static unsigned long long subtot, total;
277 static time_t start, last;
293 if ((verbose > 1 && diff >= (verbose > 3 ? 1 : 5)) || count == 0) {
297 printf("%s offset: %14llukB %5g MB/s ", op,
298 offset / ONE_KB, (double)subtot / ONE_MB / diff);
299 if (isatty_flag && count)
307 subtot = total = start = last = 0;
314 * Write a chunk to disk, handling errors, interrupted writes, etc.
316 * If there is an IO error hit during the write, it is possible that
317 * this will just show up as a short write, and a subsequent write
318 * will return the actual error. We want to continue in the face of
319 * minor media errors so that we can validate the whole device if
320 * possible, but if there are many errors we don't want to loop forever.
322 * The error count will be returned upon exit to ensure that the
323 * media errors are detected even if nobody is looking at the output.
325 * Returns 0 on success, or -ve errno on failure.
327 static size_t write_retry(int fd, const char *chunk_buf, size_t nrequested,
328 unsigned long long offset, const char *file)
333 nwritten = pwrite(fd, chunk_buf, nrequested, offset);
335 if (errno != ENOSPC) {
336 fprintf(stderr, "\n%s: write %s@%llu+%zi failed: %s\n",
337 progname, file, offset, nrequested,
339 if (error_count++ < MAX_ALLOWED_IO_ERR)
344 if (nwritten < nrequested) {
345 fprintf(stderr, "\n%s: write %s@%llu+%zi short: %ld/%ld\n",
346 progname, file, offset, nrequested, nwritten,
349 chunk_buf += nwritten;
350 nrequested -= nwritten;
358 * write_chunks: write the chunk_buf on the device. The number of write
359 * operations are based on the parameters write_end, offset, and chunksize.
361 * Returns 0 on success, or -ve error number on failure.
363 static int write_chunks(int fd, unsigned long long offset,
364 unsigned long long *write_end, char *chunk_buf,
365 size_t chunksize, const time_t time_st,
366 const ino_t inode_st, const char *file)
368 unsigned long long stride;
370 stride = full ? chunksize : ONE_GB;
371 for (offset = offset & ~(chunksize - 1); offset < *write_end;
375 if (offset + chunksize > *write_end)
376 chunksize = *write_end - offset;
377 fill_chunk(chunk_buf, chunksize, offset, time_st, inode_st);
378 ret = write_retry(fd, chunk_buf, chunksize, offset, file);
381 *write_end = offset - stride;
385 show_rate("write", offset, chunksize);
388 if (fsync(fd) == -1) {
389 fprintf(stderr, "%s: fsync failed: %s\n", progname,
397 * read_chunk: reads the chunk_buf from the device. The number of read
398 * operations are based on the parameters read_end, offset, and chunksize.
400 static int read_chunks(int fd, unsigned long long offset,
401 unsigned long long read_end, char *chunk_buf,
402 size_t chunksize, const time_t time_st,
403 const ino_t inode_st, const char *file)
405 unsigned long long stride;
407 if (ioctl(fd, BLKFLSBUF, 0) < 0 && verbose > 1)
408 fprintf(stderr, "%s: ioctl BLKFLSBUF failed: %s (ignoring)\n",
409 progname, strerror(errno));
411 stride = full ? chunksize : ONE_GB;
412 for (offset = offset & ~(chunksize - 1); offset < read_end;
416 if (offset + chunksize > read_end)
417 chunksize = read_end - offset;
420 /* reset errno before calling pread */
423 rc = pread(fd, chunk_buf + nread, chunksize - nread,
426 fprintf(stderr, "\n%s: read %s@%llu+%zi no data: %s\n",
427 progname, file, offset + nread,
428 chunksize - nread, strerror(errno));
432 fprintf(stderr, "\n%s: read %s@%llu+%zi failed: %s\n",
433 progname, file, offset + nread,
434 chunksize - nread, strerror(errno));
435 if (error_count++ < MAX_ALLOWED_IO_ERR)
440 if (nread < chunksize) {
442 "\n%s: read %s@%llu+%zi short: %zi/%zi\n",
443 progname, file, offset + nread,
444 chunksize - nread, rc, chunksize);
448 if (verify_chunk(chunk_buf, chunksize, offset, time_st,
449 inode_st, file) != 0)
452 show_rate("read", offset, chunksize);
457 static int parse_size(const char *optarg, unsigned long long *size,
458 unsigned long long size_units)
462 *size = strtoull(optarg, &end, 0);
466 case 'e': case 'E': size_units <<= 10; /* fallthrough */
467 case 'p': case 'P': size_units <<= 10; /* fallthrough */
468 case 't': case 'T': size_units <<= 10; /* fallthrough */
469 case 'g': case 'G': size_units <<= 10; /* fallthrough */
470 case 'm': case 'M': size_units <<= 10; /* fallthrough */
471 case 'k': case 'K': size_units <<= 10; break;
481 int main(int argc, char **argv)
483 unsigned long long chunksize = ONE_MB; /* IO chunk size */
484 unsigned long long offset = 0; /* offset in kB */
485 unsigned long long dev_size = 0;
486 unsigned int force = 0; /* run test run without confirmation*/
487 time_t time_st = 0; /* Default timestamp */
488 char *chunk_buf = NULL;
490 int mode = O_RDWR; /* mode which device should be opened */
494 progname = !strrchr(argv[0], '/') ? argv[0] : strrchr(argv[0], '/') + 1;
495 while ((c = getopt_long(argc, argv, "c:fhlo:pqrs:t:vw", long_opts,
499 if (parse_size(optarg, &chunksize, ONE_MB)) {
500 fprintf(stderr, "%s: bad chunksize '%s'\n",
504 if (chunksize > ONE_GB || chunksize < 4096) {
506 "%s: valid chunksize 4KB-1GB, not '%s'\n",
518 if (parse_size(optarg, &offset, ONE_KB)) {
519 fprintf(stderr, "%s: bad offset '%s'\n",
535 if (parse_size(optarg, &dev_size, ONE_MB)) {
536 fprintf(stderr, "%s: bad size '%s'\n",
542 time_st = (time_t)strtoul(optarg, NULL, 0);
557 devname = argv[optind];
559 fprintf(stderr, "%s: device name not given\n", progname);
564 if (readoption && writeoption)
566 if (!readoption && !writeoption) {
571 if (!force && writeoption) {
572 printf("%s: permanently overwrite all data on %s (yes/no)? ",
574 if (scanf("%3s", yesno) == EOF && ferror(stdin)) {
575 perror("reading from stdin");
578 if (!(strcasecmp("yes", yesno) || strcasecmp("y", yesno))) {
579 printf("Not continuing due to '%s' response", yesno);
584 if (!writeoption && time_st == 0) {
585 fprintf(stderr, "%s: must give timestamp for read-only test\n",
590 fd = open_dev(devname, mode);
591 dev_size = sizeof_dev(fd, dev_size);
593 printf("%s: %s is %llu bytes (%g GB) in size\n",
594 progname, devname, dev_size, (double)dev_size / ONE_GB);
597 fprintf(stderr, "%s: cannot test on device size < 1MB\n",
603 if (offset >= dev_size - chunksize) {
604 fprintf(stderr, "%s: offset %llu >= device size %llu\n",
605 progname, offset, dev_size);
610 (void)time(&time_st);
612 isatty_flag = isatty(STDOUT_FILENO);
615 printf("timestamp: %lu chunksize: %llu size: %llu\n",
616 time_st, chunksize, dev_size);
618 chunk_buf = (char *)calloc(chunksize, 1);
620 fprintf(stderr, "%s: memory allocation failed for chunk_buf\n",
626 c = write_chunks(fd, offset, &dev_size, chunk_buf, chunksize,
627 time_st, ino_st, devname);
628 if (c < 0 && c != -ENOSPC) {
632 if (!full && c != -ENOSPC) { /* end of device block-aligned */
633 offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
635 c = write_chunks(fd, offset, &dev_size, chunk_buf,
636 chunksize, time_st, ino_st, devname);
637 if (c < 0 && c != -ENOSPC) {
642 show_rate("write", dev_size, 0);
644 printf("write complete\n");
647 if (read_chunks(fd, offset, dev_size, chunk_buf, chunksize,
648 time_st, ino_st, devname)) {
652 if (!full && c != -ENOSPC) { /* end of device block-aligned */
653 offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
655 if (read_chunks(fd, offset, dev_size, chunk_buf,
656 chunksize, time_st, ino_st, devname)) {
661 show_rate("read", dev_size, 0);
663 printf("read complete\n");
665 printf("%s: data verified successfully\n", progname);