Whamcloud - gitweb
e05f41b7da23aab88d11b6c9fc6a7776e41ee480
[fs/lustre-release.git] / lustre / utils / llverdev.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
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.
9  *
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).
15  *
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
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2011, Intel Corporation.
27  *
28  */
29 /*
30  * This file is part of Lustre, http://www.lustre.org/
31  * Lustre is a trademark of Sun Microsystems, Inc.
32  *
33  * lustre/utils/llverdev.c
34  *
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
39  * 1. full mode
40  * 2. partial mode
41  *
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.
46  *
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.
50  *
51  * A chunk buffer with default size of 1MB is used to write and read test
52  * pattern in bulk.
53  */
54
55 #ifndef _GNU_SOURCE
56 #define _GNU_SOURCE
57 #endif
58 #ifndef LUSTRE_UTILS
59 #define LUSTRE_UTILS
60 #endif
61 #ifndef _LARGEFILE64_SOURCE
62 #define _LARGEFILE64_SOURCE
63 #endif
64 #ifndef _FILE_OFFSET_BITS
65 #define _FILE_OFFSET_BITS 64
66 #endif
67
68 #include <features.h>
69 #include <stdlib.h>
70 #include <stdio.h>
71 #include <string.h>
72 #include <ctype.h>
73 #include <fcntl.h>
74 #include <unistd.h>
75 #include <limits.h>
76 #include <errno.h>
77 #include <fcntl.h>
78 #include <getopt.h>
79 #include <time.h>
80 #include <sys/types.h>
81 #include <sys/stat.h>
82 #include <sys/ioctl.h>
83 #include <sys/mount.h>
84 #include <sys/time.h>
85 #include <gnu/stubs.h>
86
87 #ifdef HAVE_EXT2FS_EXT2FS_H
88 #  include <ext2fs/ext2fs.h>
89 #endif
90
91 #define ONE_MB (1024 * 1024)
92 #define ONE_GB (1024 * 1024 * 1024)
93 #define HALF_MB (ONE_MB / 2)
94 #define ONE_KB 1024
95 #define HALF_KB (ONE_KB / 2)
96 #define BLOCKSIZE 4096
97
98 /* Structure for writting test pattern */
99 struct block_data {
100         unsigned long long bd_offset;
101         unsigned long long bd_time;
102         unsigned long long bd_inode;
103 };
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;
112
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 },
126         { .name = NULL } };
127
128 /*
129  * Usage: displays help information, whenever user supply --help option in
130  * command or enters incorrect command line.
131  */
132 void usage(int status)
133 {
134         if (status != 0) {
135                 printf("\nUsage: %s [OPTION]... <device-name> ...\n",
136                        progname);
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"
144                        "\t-v, --verbose\n"
145                        "\t-q, --quiet\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");
151         }
152         exit(status);
153 }
154
155 /*
156  * Open_dev: Opens device in specified mode and returns fd.
157  */
158 static int open_dev(const char *devname, int mode)
159 {
160         int     fd;
161 #ifdef HAVE_EXT2FS_EXT2FS_H
162         int     mount_flags;
163         char    mountpt[80] = "";
164
165         if (ext2fs_check_mount_point(devname, &mount_flags, mountpt,
166                                      sizeof(mountpt))) {
167                 fprintf(stderr, "%s: ext2fs_check_mount_point failed:%s",
168                         progname, strerror(errno));
169                 exit(1);
170         }
171         if (mount_flags & EXT2_MF_MOUNTED){
172                 fprintf(stderr, "%s: %s is already mounted\n", progname,
173                         devname);
174                 exit(1);
175         }
176 #endif
177         fd = open(devname, mode | O_EXCL | O_LARGEFILE);
178         if (fd < 0) {
179                 fprintf(stderr, "%s: Open failed: %s",progname,strerror(errno));
180                 exit(3);
181         }
182         return fd;
183 }
184
185 #ifdef HAVE_BLKID_BLKID_H
186 #include <blkid/blkid.h>
187 #endif
188 /*
189  * sizeof_dev: Returns size of device in bytes
190  */
191 static loff_t sizeof_dev(int fd)
192 {
193         loff_t numbytes;
194
195 #ifdef HAVE_BLKID_BLKID_H
196         numbytes = blkid_get_dev_size(fd);
197         if (numbytes <= 0) {
198                 fprintf(stderr, "%s: blkid_get_dev_size(%s) failed",
199                         progname, devname);
200                 return 1;
201         }
202         goto out;
203 #else
204 # if defined BLKGETSIZE64       /* in sys/mount.h */
205         if (ioctl(fd, BLKGETSIZE64, &numbytes) >= 0)
206                 goto out;
207 # endif
208 # if defined BLKGETSIZE         /* in sys/mount.h */
209         {
210                 unsigned long sectors;
211
212                 if (ioctl(fd, BLKGETSIZE, &sectors) >= 0) {
213                         numbytes = (loff_t)sectors << 9;
214                         goto out;
215                 }
216         }
217 # endif
218         {
219                 struct stat statbuf;
220
221                 if (fstat(fd, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) {
222                         numbytes = statbuf.st_size;
223                         goto out;
224                 }
225         }
226         fprintf(stderr, "%s: unable to determine size of %s\n",
227                 progname, devname);
228         return 0;
229 #endif
230
231 out:
232         if (verbose)
233                 printf("%s: %s is %llu bytes (%g GB) in size\n",
234                        progname, devname,
235                        (unsigned long long)numbytes, (double)numbytes / ONE_GB);
236
237         return numbytes;
238 }
239
240 /*
241  * Verify_chunk: Verifies test pattern in each 4kB (BLOCKSIZE) is correct.
242  * Returns 0 if test offset and timestamp is correct otherwise 1.
243  */
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)
247 {
248         struct block_data *bd;
249         char *chunk_end;
250
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))
257                         continue;
258
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);
263                 error_count++;
264                 return 1;
265         }
266         return 0;
267 }
268
269 /*
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.
273  */
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)
276 {
277         struct block_data *bd;
278         char *chunk_end;
279
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;
287         }
288 }
289
290 void show_rate(char *op, unsigned long long offset, unsigned long long *count)
291 {
292         static time_t last;
293         time_t now;
294         double diff;
295
296         now = time(NULL);
297         diff = now - last;
298
299         if (diff > 4) {
300                 if (last != 0) {
301                         if (isatty_flag)
302                                 printf("\r");
303                         printf("%s offset: %14llukB %5g MB/s            ", op,
304                                offset / ONE_KB, (double)(*count) /ONE_MB /diff);
305                         if (isatty_flag)
306                                 fflush(stdout);
307                         else
308                                 printf("\n");
309
310                         *count = 0;
311                 }
312                 last = now;
313         }
314 }
315
316 /*
317  * Write a chunk to disk, handling errors, interrupted writes, etc.
318  *
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.
324  *
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.
327  *
328  * Returns 0 on success, or -ve errno on failure.
329  */
330 size_t write_retry(int fd, const char *chunk_buf, size_t nrequested,
331                    unsigned long long offset, const char *file)
332 {
333         long nwritten;
334
335 retry:
336         nwritten = write(fd, chunk_buf, nrequested);
337         if (nwritten < 0) {
338                 if (errno != ENOSPC) {
339                         fprintf(stderr, "\n%s: write %s@%llu+%zi failed: %s\n",
340                                 progname, file, offset, nrequested,
341                                 strerror(errno));
342                         if (error_count++ < 100)
343                                 return 0;
344                 }
345                 return -errno;
346         }
347         if (nwritten < nrequested) {
348                 fprintf(stderr, "\n%s: write %s@%llu+%zi short: %ld written\n",
349                         progname, file, offset, nrequested, nwritten);
350                 offset += nwritten;
351                 chunk_buf += nwritten;
352                 nrequested -= nwritten;
353                 goto retry;
354         }
355
356         return 0;
357 }
358
359 /*
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.
362  *
363  * Returns 0 on success, or -ve error number on failure.
364  */
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)
368 {
369         unsigned long long stride, count = 0;
370
371         stride = full ? chunksize : (ONE_GB - chunksize);
372         for (offset = offset & ~(chunksize - 1); offset < write_end;
373              offset += stride) {
374                 int ret;
375
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));
379                         return -errno;
380                 }
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,
385                                    inode_st);
386                         ret = write_retry(fd, chunk_buf, chunksize,
387                                           offset, file);
388                         if (ret < 0)
389                                 return ret;
390                         offset += chunksize;
391                         count += chunksize;
392                         if (offset + chunksize > write_end)
393                                 chunksize = write_end - offset;
394                 }
395                 fill_chunk(chunk_buf, chunksize, offset, time_st, inode_st);
396                 ret = write_retry(fd, chunk_buf, chunksize, offset, file);
397                 if (ret < 0)
398                         return ret;
399
400                 count += chunksize;
401                 if (verbose > 1)
402                         show_rate("write", offset, &count);
403         }
404
405         if (verbose > 1) {
406                 show_rate("write", offset, &count);
407                 printf("\nwrite complete\n");
408         }
409         if (fsync(fd) == -1) {
410                 fprintf(stderr, "%s: fsync failed: %s\n", progname,
411                         strerror(errno));
412                 return -errno;
413         }
414         return 0;
415 }
416
417 /*
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.
420  */
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)
424 {
425         unsigned long long stride, count = 0;
426
427         if (ioctl(fd, BLKFLSBUF, 0) < 0 && verbose)
428                 fprintf(stderr, "%s: ioctl BLKFLSBUF failed: %s (ignoring)\n",
429                         progname, strerror(errno));
430
431         stride = full ? chunksize : (ONE_GB - chunksize);
432         for (offset = offset & ~(chunksize - 1); offset < read_end;
433              offset += stride) {
434                 ssize_t nread;
435
436                 if (lseek64(fd, offset, SEEK_SET) == -1) {
437                         fprintf(stderr, "\n%s: lseek64(%llu) failed: %s\n",
438                                 progname, offset, strerror(errno));
439                         return 1;
440                 }
441                 if (offset + chunksize > read_end)
442                         chunksize = read_end - offset;
443
444                 if (!full && offset > chunksize) {
445                         nread = read(fd, chunk_buf, chunksize);
446                         if (nread < 0) {
447                                 fprintf(stderr,"\n%s: read %s@%llu+%zi failed: "
448                                         "%s\n", progname, file, offset,
449                                         chunksize, strerror(errno));
450                                 error_count++;
451                                 return 1;
452                         }
453                         if (nread < chunksize) {
454                                 fprintf(stderr, "\n%s: read %s@%llu+%zi short: "
455                                         "%zi read\n", progname, file, offset,
456                                         chunksize, nread);
457                                 error_count++;
458                         }
459                         if (verify_chunk(chunk_buf, nread, offset, time_st,
460                                          inode_st, file) != 0)
461                                 return 1;
462                         offset += chunksize;
463                         count += chunksize;
464
465                         /* Need to reset position after read error */
466                         if (nread < chunksize &&
467                             lseek64(fd, offset, SEEK_SET) == -1) {
468                                 fprintf(stderr,
469                                         "\n%s: lseek64(%s@%llu) failed: %s\n",
470                                         progname, file, offset,strerror(errno));
471                                 return 1;
472                         }
473                         if (offset + chunksize >= read_end)
474                                 chunksize = read_end - offset;
475                 }
476
477                 nread = read(fd, chunk_buf, chunksize);
478                 if (nread < 0) {
479                         fprintf(stderr, "\n%s: read failed: %s\n", progname,
480                                 strerror(errno));
481                         error_count++;
482                         return 1;
483                 }
484                 if (nread < chunksize) {
485                         fprintf(stderr, "\n%s: read %s@%llu+%zi short: "
486                                 "%zi read\n", progname, file, offset,
487                                 chunksize, nread);
488                         error_count++;
489                 }
490
491                 if (verify_chunk(chunk_buf, nread, offset, time_st,
492                                  inode_st, file) != 0)
493                         return 1;
494
495                 count += chunksize;
496                 if (verbose > 1)
497                         show_rate("read", offset, &count);
498         }
499         if (verbose > 1) {
500                 show_rate("read", offset, &count);
501                 printf("\nread complete\n");
502         }
503         return 0;
504 }
505
506 int main(int argc, char **argv)
507 {
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;
514         char yesno[4];
515         int mode = O_RDWR;              /* mode which device should be opened */
516         int fd;
517         int error = 0, c;
518
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,
522                                 NULL)) != -1) {
523                 switch (c) {
524                 case 'c':
525                         chunksize = (strtoul(optarg, NULL, 0) * ONE_MB);
526                         if (!chunksize) {
527                                 fprintf(stderr, "%s: chunk size value should be"
528                                         "nonzero and multiple of 1MB\n",
529                                         progname);
530                                 return -1;
531                         }
532                         break;
533                 case 'f':
534                         force = 1;
535                         break;
536                 case 'l':
537                         full = 1;
538                         break;
539                 case 'o':
540                         offset = strtoull(optarg, NULL, 0) * ONE_KB;
541                         break;
542                 case 'p':
543                         full = 0;
544                         break;
545                 case 'q':
546                         verbose = 0;
547                         break;
548                 case 'r':
549                         readoption = 1;
550                         mode = O_RDONLY;
551                         break;
552                 case 't':
553                         time_st = (time_t)strtoul(optarg, NULL, 0);
554                         break;
555                 case 'v':
556                         verbose++;
557                         break;
558                 case 'w':
559                         writeoption = 1;
560                         mode = O_WRONLY;
561                         break;
562                 case 'h':
563                 default:
564                         usage (1);
565                         return 0;
566                 }
567         }
568         offset_orig = offset;
569         devname = argv[optind];
570         if (!devname) {
571                 fprintf(stderr, "%s: device name not given\n", progname);
572                 usage (1);
573                 return -1;
574         }
575
576         if (readoption && writeoption)
577                 mode = O_RDWR;
578         if (!readoption && !writeoption) {
579                 readoption = 1;
580                 writeoption = 1;
581         }
582
583         if (!force && writeoption) {
584                 printf("%s: permanently overwrite all data on %s (yes/no)? ",
585                        progname, devname);
586                 if (scanf("%3s", yesno) == EOF && ferror(stdin)) {
587                         perror("reading from stdin");
588                         return -1;
589                 }
590                 if (!(strcasecmp("yes", yesno) || strcasecmp("y", yesno))) {
591                         printf("Not continuing due to '%s' response", yesno);
592                         return 0;
593                 }
594         }
595
596         if (!writeoption && time_st == 0) {
597                 fprintf(stderr, "%s: must give timestamp for read-only test\n",
598                         progname);
599                 usage(1);
600         }
601
602         fd = open_dev(devname, mode);
603         dev_size = sizeof_dev(fd);
604         if (!dev_size) {
605                 fprintf(stderr, "%s: cannot test on device size < 1MB\n",
606                         progname);
607                 error = 7;
608                 goto close_dev;
609         }
610
611         if (dev_size < (offset * 2)) {
612                 fprintf(stderr, "%s: device size %llu < offset %llu\n",
613                         progname, dev_size, offset);
614                 error = 6;
615                 goto close_dev;
616         }
617         if (!time_st)
618                 (void)time(&time_st);
619
620         isatty_flag = isatty(STDOUT_FILENO);
621
622         if (verbose)
623                 printf("Timestamp: %lu\n", time_st);
624
625         chunk_buf = (char *)calloc(chunksize, 1);
626         if (chunk_buf == NULL) {
627                 fprintf(stderr, "%s: memory allocation failed for chunk_buf\n",
628                         progname);
629                 error = 4;
630                 goto close_dev;
631         }
632         if (writeoption) {
633                 c = write_chunks(fd, offset, dev_size, chunk_buf, chunksize,
634                                  time_st, 0, devname);
635                 if (c < 0 && c != -ENOSPC) {
636                         error = 3;
637                         goto chunk_buf;
638                 }
639                 if (!full) { /* end of device aligned to a block */
640                         offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
641                                   ~(BLOCKSIZE - 1));
642                         c = write_chunks(fd, offset, dev_size, chunk_buf,
643                                          chunksize, time_st, 0, devname);
644                         if (c < 0 && c != -ENOSPC) {
645                                 error = 3;
646                                 goto chunk_buf;
647                         }
648                 }
649                 offset = offset_orig;
650         }
651         if (readoption) {
652                 if (read_chunks(fd, offset, dev_size, chunk_buf, chunksize,
653                                 time_st, 0, devname)) {
654                         error = 2;
655                         goto chunk_buf;
656                 }
657                 if (!full) { /* end of device aligned to a block */
658                         offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
659                                   ~(BLOCKSIZE - 1));
660                         if (read_chunks(fd, offset, dev_size, chunk_buf,
661                                         chunksize, time_st, 0, devname)) {
662                                 error = 2;
663                                 goto chunk_buf;
664                         }
665                 }
666                 if (verbose)
667                         printf("\n%s: data verified successfully\n", progname);
668         }
669         error = error_count;
670 chunk_buf:
671         free(chunk_buf);
672 close_dev:
673         close(fd);
674         return error;
675 }