Whamcloud - gitweb
c53a15287384e214c2a93db74509f4ffce27254c
[fs/lustre-release.git] / lustre / utils / llverdev.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lustre/utils/llverdev.c
37  *
38  * Large Block Device Verification Tool.
39  * This program is used to test whether the block device is correctly
40  * handling IO beyond 2TB boundary.
41  * This tool have two working modes
42  * 1. full mode
43  * 2. partial mode
44  *
45  * In full mode, the program writes a test pattern on the entire disk.
46  * The test pattern (device offset and timestamp) is written at the
47  * beginning of each 4kB block. When the whole device is full the read
48  * operation is performed to verify that the test pattern is correct.
49  *
50  * In partial mode, the program writes data at the critical locations
51  * of the device such as start of the device, before and after multiple of 1GB
52  * offset and at the end.
53  *
54  * A chunk buffer with default size of 1MB is used to write and read test
55  * pattern in bulk.
56  */
57
58 #ifndef _GNU_SOURCE
59 #define _GNU_SOURCE
60 #endif
61 #ifndef LUSTRE_UTILS
62 #define LUSTRE_UTILS
63 #endif
64 #ifndef _LARGEFILE64_SOURCE
65 #define _LARGEFILE64_SOURCE
66 #endif
67 #ifndef _FILE_OFFSET_BITS
68 #define _FILE_OFFSET_BITS 64
69 #endif
70
71 #include <features.h>
72 #include <stdlib.h>
73 #include <stdio.h>
74 #include <string.h>
75 #include <ctype.h>
76 #include <fcntl.h>
77 #include <unistd.h>
78 #include <limits.h>
79 #include <errno.h>
80 #include <fcntl.h>
81 #include <getopt.h>
82 #include <time.h>
83 #include <sys/types.h>
84 #include <sys/stat.h>
85 #include <sys/ioctl.h>
86 #include <sys/mount.h>
87 #include <sys/time.h>
88 #include <gnu/stubs.h>
89
90 #ifdef HAVE_EXT2FS_EXT2FS_H
91 #  include <ext2fs/ext2fs.h>
92 #endif
93
94 #define ONE_MB (1024 * 1024)
95 #define ONE_GB (1024 * 1024 * 1024)
96 #define HALF_MB (ONE_MB / 2)
97 #define ONE_KB 1024
98 #define HALF_KB (ONE_KB / 2)
99 #define BLOCKSIZE 4096
100
101 /* Structure for writting test pattern */
102 struct block_data {
103         unsigned long long bd_offset;
104         unsigned long long bd_time;
105         unsigned long long bd_inode;
106 };
107 static char *progname;          /* name by which this program was run. */
108 static unsigned verbose = 1;    /* prints offset in kB, operation rate */
109 static int readoption;          /* run test in read-only (verify) mode */
110 static int writeoption;         /* run test in write_only mode */
111 const char *devname;            /* name of device to be tested. */
112 static unsigned full = 1;       /* flag to full check */
113 static int error_count;         /* number of IO errors hit during run */
114 static int isatty_flag;
115
116 static struct option const longopts[] =
117 {
118         { "chunksize", required_argument, 0, 'c' },
119         { "force", no_argument, 0, 'f' },
120         { "help", no_argument, 0, 'h' },
121         { "offset", required_argument, 0, 'o' },
122         { "partial", required_argument, 0, 'p' },
123         { "quiet", required_argument, 0, 'q' },
124         { "read", no_argument, 0, 'r' },
125         { "timestamp", required_argument, 0, 't' },
126         { "verbose", no_argument, 0, 'v' },
127         { "write", no_argument, 0, 'w' },
128         { "long", no_argument, 0, 'l' },
129         { "full", no_argument, 0, 'l' },
130         { 0, 0, 0, 0}
131 };
132
133 /*
134  * Usage: displays help information, whenever user supply --help option in
135  * command or enters incorrect command line.
136  */
137 void usage(int status)
138 {
139         if (status != 0) {
140                 printf("\nUsage: %s [OPTION]... <device-name> ...\n",
141                        progname);
142                 printf("Block device verification tool.\n"
143                        "\t-t {seconds}, --timestamp, "
144                        "set test time  (default=current time())\n"
145                        "\t-o {offset}, --offset, "
146                        "offset in kB of start of test, default=0\n"
147                        "\t-r, --read, run in verify mode\n"
148                        "\t-w, --write, run in test-pattern mode, default=rw\n"
149                        "\t-v, --verbose\n"
150                        "\t-q, --quiet\n"
151                        "\t-l, --long, --full check of device\n"
152                        "\t-p, --partial, for partial check (1GB steps)\n"
153                        "\t-c {bytes}, --chunksize, IO size, default=1048576\n"
154                        "\t-f, --force, force test to run without confirmation\n"
155                        "\t-h, --help, display this help and exit\n");
156         }
157         exit(status);
158 }
159
160 /*
161  * Open_dev: Opens device in specified mode and returns fd.
162  */
163 static int open_dev(const char *devname, int mode)
164 {
165         int     fd;
166 #ifdef HAVE_EXT2FS_EXT2FS_H
167         int     mount_flags;
168         char    mountpt[80] = "";
169
170         if (ext2fs_check_mount_point(devname, &mount_flags, mountpt,
171                                      sizeof(mountpt))) {
172                 fprintf(stderr, "%s: ext2fs_check_mount_point failed:%s",
173                         progname, strerror(errno));
174                 exit(1);
175         }
176         if (mount_flags & EXT2_MF_MOUNTED){
177                 fprintf(stderr, "%s: %s is already mounted\n", progname,
178                         devname);
179                 exit(1);
180         }
181 #endif
182         fd = open(devname, mode | O_EXCL | O_LARGEFILE);
183         if (fd < 0) {
184                 fprintf(stderr, "%s: Open failed: %s",progname,strerror(errno));
185                 exit(3);
186         }
187         return fd;
188 }
189
190 #ifdef HAVE_BLKID_BLKID_H
191 #include <blkid/blkid.h>
192 #endif
193 /*
194  * sizeof_dev: Returns size of device in bytes
195  */
196 static loff_t sizeof_dev(int fd)
197 {
198         loff_t numbytes;
199
200 #ifdef HAVE_BLKID_BLKID_H
201         numbytes = blkid_get_dev_size(fd);
202         if (numbytes <= 0) {
203                 fprintf(stderr, "%s: blkid_get_dev_size(%s) failed",
204                         progname, devname);
205                 return 1;
206         }
207         goto out;
208 #else
209 # if defined BLKGETSIZE64       /* in sys/mount.h */
210         if (ioctl(fd, BLKGETSIZE64, &numbytes) >= 0)
211                 goto out;
212 # endif
213 # if defined BLKGETSIZE         /* in sys/mount.h */
214         {
215                 unsigned long sectors;
216
217                 if (ioctl(fd, BLKGETSIZE, &sectors) >= 0) {
218                         numbytes = (loff_t)sectors << 9;
219                         goto out;
220                 }
221         }
222 # endif
223         {
224                 struct stat statbuf;
225
226                 if (fstat(fd, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) {
227                         numbytes = statbuf.st_size;
228                         goto out;
229                 }
230         }
231         fprintf(stderr, "%s: unable to determine size of %s\n",
232                 progname, devname);
233         return 0;
234 #endif
235
236 out:
237         if (verbose)
238                 printf("%s: %s is %llu bytes (%g GB) in size\n",
239                        progname, devname,
240                        (unsigned long long)numbytes, (double)numbytes / ONE_GB);
241
242         return numbytes;
243 }
244
245 /*
246  * Verify_chunk: Verifies test pattern in each 4kB (BLOCKSIZE) is correct.
247  * Returns 0 if test offset and timestamp is correct otherwise 1.
248  */
249 int verify_chunk(char *chunk_buf, const size_t chunksize,
250                  unsigned long long chunk_off, const unsigned long long time_st,
251                  const unsigned long long inode_st, const char *file)
252 {
253         struct block_data *bd;
254         char *chunk_end;
255
256         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
257              (char *)chunk_buf < chunk_end;
258              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
259                 bd = (struct block_data *)chunk_buf;
260                 if ((bd->bd_offset == chunk_off) && (bd->bd_time == time_st) &&
261                     (bd->bd_inode == inode_st))
262                         continue;
263
264                 fprintf(stderr, "\n%s: verify %s failed offset/timestamp/inode "
265                         "%llu/%llu/%llu: found %llu/%llu/%llu instead\n",
266                         progname, file, chunk_off, time_st, inode_st,
267                         bd->bd_offset, bd->bd_time, bd->bd_inode);
268                 error_count++;
269                 return 1;
270         }
271         return 0;
272 }
273
274 /*
275  * fill_chunk: Fills the chunk with current or user specified timestamp
276  * and offset. The test pattern is filled at the beginning of
277  * each 4kB(BLOCKSIZE) blocks in chunk_buf.
278  */
279 void fill_chunk(char *chunk_buf, size_t chunksize, loff_t chunk_off,
280                 const time_t time_st, const ino_t inode_st)
281 {
282         struct block_data *bd;
283         char *chunk_end;
284
285         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
286              (char *)chunk_buf < chunk_end;
287              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
288                 bd = (struct block_data *)chunk_buf;
289                 bd->bd_offset = chunk_off;
290                 bd->bd_time = time_st;
291                 bd->bd_inode = inode_st;
292         }
293 }
294
295 void show_rate(char *op, unsigned long long offset, unsigned long long *count)
296 {
297         static time_t last;
298         time_t now;
299         double diff;
300
301         now = time(NULL);
302         diff = now - last;
303
304         if (diff > 4) {
305                 if (last != 0) {
306                         if (isatty_flag)
307                                 printf("\r");
308                         printf("%s offset: %14llukB %5g MB/s            ", op,
309                                offset / ONE_KB, (double)(*count) /ONE_MB /diff);
310                         if (isatty_flag)
311                                 fflush(stdout);
312                         else
313                                 printf("\n");
314
315                         *count = 0;
316                 }
317                 last = now;
318         }
319 }
320
321 /*
322  * Write a chunk to disk, handling errors, interrupted writes, etc.
323  *
324  * If there is an IO error hit during the write, it is possible that
325  * this will just show up as a short write, and a subsequent write
326  * will return the actual error.  We want to continue in the face of
327  * minor media errors so that we can validate the whole device if
328  * possible, but if there are many errors we don't want to loop forever.
329  *
330  * The error count will be returned upon exit to ensure that the
331  * media errors are detected even if nobody is looking at the output.
332  *
333  * Returns 0 on success, or -ve errno on failure.
334  */
335 size_t write_retry(int fd, const char *chunk_buf, size_t nrequested,
336                    unsigned long long offset, const char *file)
337 {
338         long nwritten;
339
340 retry:
341         nwritten = write(fd, chunk_buf, nrequested);
342         if (nwritten < 0) {
343                 if (errno != ENOSPC) {
344                         fprintf(stderr, "\n%s: write %s@%llu+%zi failed: %s\n",
345                                 progname, file, offset, nrequested,
346                                 strerror(errno));
347                         if (error_count++ < 100)
348                                 return 0;
349                 }
350                 return -errno;
351         }
352         if (nwritten < nrequested) {
353                 fprintf(stderr, "\n%s: write %s@%llu+%zi short: %ld written\n",
354                         progname, file, offset, nrequested, nwritten);
355                 offset += nwritten;
356                 chunk_buf += nwritten;
357                 nrequested -= nwritten;
358                 goto retry;
359         }
360
361         return 0;
362 }
363
364 /*
365  * write_chunks: write the chunk_buf on the device. The number of write
366  * operations are based on the parameters write_end, offset, and chunksize.
367  *
368  * Returns 0 on success, or -ve error number on failure.
369  */
370 int write_chunks(int fd, unsigned long long offset,unsigned long long write_end,
371                  char *chunk_buf, size_t chunksize, const time_t time_st,
372                  const ino_t inode_st, const char *file)
373 {
374         unsigned long long stride, count = 0;
375
376         stride = full ? chunksize : (ONE_GB - chunksize);
377         for (offset = offset & ~(chunksize - 1); offset < write_end;
378              offset += stride) {
379                 int ret;
380
381                 if (lseek64(fd, offset, SEEK_SET) == -1) {
382                         fprintf(stderr, "\n%s: lseek64(%s+%llu) failed: %s\n",
383                                 progname, file, offset, strerror(errno));
384                         return -errno;
385                 }
386                 if (offset + chunksize > write_end)
387                         chunksize = write_end - offset;
388                 if (!full && offset > chunksize) {
389                         fill_chunk(chunk_buf, chunksize, offset, time_st,
390                                    inode_st);
391                         ret = write_retry(fd, chunk_buf, chunksize,
392                                           offset, file);
393                         if (ret < 0)
394                                 return ret;
395                         offset += chunksize;
396                         count += chunksize;
397                         if (offset + chunksize > write_end)
398                                 chunksize = write_end - offset;
399                 }
400                 fill_chunk(chunk_buf, chunksize, offset, time_st, inode_st);
401                 ret = write_retry(fd, chunk_buf, chunksize, offset, file);
402                 if (ret < 0)
403                         return ret;
404
405                 count += chunksize;
406                 if (verbose > 1)
407                         show_rate("write", offset, &count);
408         }
409
410         if (verbose > 1) {
411                 show_rate("write", offset, &count);
412                 printf("\nwrite complete\n");
413         }
414         if (fsync(fd) == -1) {
415                 fprintf(stderr, "%s: fsync failed: %s\n", progname,
416                         strerror(errno));
417                 return -errno;
418         }
419         return 0;
420 }
421
422 /*
423  * read_chunk: reads the chunk_buf from the device. The number of read
424  * operations are based on the parameters read_end, offset, and chunksize.
425  */
426 int read_chunks(int fd, unsigned long long offset, unsigned long long read_end,
427                 char *chunk_buf, size_t chunksize, const time_t time_st,
428                 const ino_t inode_st, const char *file)
429 {
430         unsigned long long stride, count = 0;
431
432         if (ioctl(fd, BLKFLSBUF, 0) < 0 && verbose)
433                 fprintf(stderr, "%s: ioctl BLKFLSBUF failed: %s (ignoring)\n",
434                         progname, strerror(errno));
435
436         stride = full ? chunksize : (ONE_GB - chunksize);
437         for (offset = offset & ~(chunksize - 1); offset < read_end;
438              offset += stride) {
439                 ssize_t nread;
440
441                 if (lseek64(fd, offset, SEEK_SET) == -1) {
442                         fprintf(stderr, "\n%s: lseek64(%llu) failed: %s\n",
443                                 progname, offset, strerror(errno));
444                         return 1;
445                 }
446                 if (offset + chunksize > read_end)
447                         chunksize = read_end - offset;
448
449                 if (!full && offset > chunksize) {
450                         nread = read(fd, chunk_buf, chunksize);
451                         if (nread < 0) {
452                                 fprintf(stderr,"\n%s: read %s@%llu+%zi failed: "
453                                         "%s\n", progname, file, offset,
454                                         chunksize, strerror(errno));
455                                 error_count++;
456                                 return 1;
457                         }
458                         if (nread < chunksize) {
459                                 fprintf(stderr, "\n%s: read %s@%llu+%zi short: "
460                                         "%zi read\n", progname, file, offset,
461                                         chunksize, nread);
462                                 error_count++;
463                         }
464                         if (verify_chunk(chunk_buf, nread, offset, time_st,
465                                          inode_st, file) != 0)
466                                 return 1;
467                         offset += chunksize;
468                         count += chunksize;
469
470                         /* Need to reset position after read error */
471                         if (nread < chunksize &&
472                             lseek64(fd, offset, SEEK_SET) == -1) {
473                                 fprintf(stderr,
474                                         "\n%s: lseek64(%s@%llu) failed: %s\n",
475                                         progname, file, offset,strerror(errno));
476                                 return 1;
477                         }
478                         if (offset + chunksize >= read_end)
479                                 chunksize = read_end - offset;
480                 }
481
482                 nread = read(fd, chunk_buf, chunksize);
483                 if (nread < 0) {
484                         fprintf(stderr, "\n%s: read failed: %s\n", progname,
485                                 strerror(errno));
486                         error_count++;
487                         return 1;
488                 }
489                 if (nread < chunksize) {
490                         fprintf(stderr, "\n%s: read %s@%llu+%zi short: "
491                                 "%zi read\n", progname, file, offset,
492                                 chunksize, nread);
493                         error_count++;
494                 }
495
496                 if (verify_chunk(chunk_buf, nread, offset, time_st,
497                                  inode_st, file) != 0)
498                         return 1;
499
500                 count += chunksize;
501                 if (verbose > 1)
502                         show_rate("read", offset, &count);
503         }
504         if (verbose > 1) {
505                 show_rate("read", offset, &count);
506                 printf("\nread complete\n");
507         }
508         return 0;
509 }
510
511 int main(int argc, char **argv)
512 {
513         time_t time_st = 0;             /* Default timestamp */
514         long long offset = 0, offset_orig; /* offset in kB */
515         size_t chunksize = ONE_MB;      /* IO chunk size */
516         char *chunk_buf = NULL;
517         unsigned int force = 0;         /* run test run without confirmation*/
518         unsigned long long dev_size = 0;
519         char yesno[4];
520         int mode = O_RDWR;              /* mode which device should be opened */
521         int fd;
522         int error = 0, c;
523
524         progname = strrchr(argv[0], '/') == NULL ?
525                 argv[0] : strrchr(argv[0], '/') + 1;
526         while ((c = getopt_long(argc, argv, "c:fhlo:pqrt:vw", longopts,
527                                 NULL)) != -1) {
528                 switch (c) {
529                 case 'c':
530                         chunksize = (strtoul(optarg, NULL, 0) * ONE_MB);
531                         if (!chunksize) {
532                                 fprintf(stderr, "%s: chunk size value should be"
533                                         "nonzero and multiple of 1MB\n",
534                                         progname);
535                                 return -1;
536                         }
537                         break;
538                 case 'f':
539                         force = 1;
540                         break;
541                 case 'l':
542                         full = 1;
543                         break;
544                 case 'o':
545                         offset = strtoull(optarg, NULL, 0) * ONE_KB;
546                         break;
547                 case 'p':
548                         full = 0;
549                         break;
550                 case 'q':
551                         verbose = 0;
552                         break;
553                 case 'r':
554                         readoption = 1;
555                         mode = O_RDONLY;
556                         break;
557                 case 't':
558                         time_st = (time_t)strtoul(optarg, NULL, 0);
559                         break;
560                 case 'v':
561                         verbose++;
562                         break;
563                 case 'w':
564                         writeoption = 1;
565                         mode = O_WRONLY;
566                         break;
567                 case 'h':
568                 default:
569                         usage (1);
570                         return 0;
571                 }
572         }
573         offset_orig = offset;
574         devname = argv[optind];
575         if (!devname) {
576                 fprintf(stderr, "%s: device name not given\n", progname);
577                 usage (1);
578                 return -1;
579         }
580
581         if (readoption && writeoption)
582                 mode = O_RDWR;
583         if (!readoption && !writeoption) {
584                 readoption = 1;
585                 writeoption = 1;
586         }
587
588         if (!force && writeoption) {
589                 printf("%s: permanently overwrite all data on %s (yes/no)? ",
590                        progname, devname);
591                 if (scanf("%3s", yesno) == EOF && ferror(stdin)) {
592                         perror("reading from stdin");
593                         return -1;
594                 }
595                 if (!(strcasecmp("yes", yesno) || strcasecmp("y", yesno))) {
596                         printf("Not continuing due to '%s' response", yesno);
597                         return 0;
598                 }
599         }
600
601         if (!writeoption && time_st == 0) {
602                 fprintf(stderr, "%s: must give timestamp for read-only test\n",
603                         progname);
604                 usage(1);
605         }
606
607         fd = open_dev(devname, mode);
608         dev_size = sizeof_dev(fd);
609         if (!dev_size) {
610                 fprintf(stderr, "%s: cannot test on device size < 1MB\n",
611                         progname);
612                 error = 7;
613                 goto close_dev;
614         }
615
616         if (dev_size < (offset * 2)) {
617                 fprintf(stderr, "%s: device size %llu < offset %llu\n",
618                         progname, dev_size, offset);
619                 error = 6;
620                 goto close_dev;
621         }
622         if (!time_st)
623                 (void)time(&time_st);
624
625         isatty_flag = isatty(STDOUT_FILENO);
626
627         if (verbose)
628                 printf("Timestamp: %lu\n", time_st);
629
630         chunk_buf = (char *)calloc(chunksize, 1);
631         if (chunk_buf == NULL) {
632                 fprintf(stderr, "%s: memory allocation failed for chunk_buf\n",
633                         progname);
634                 error = 4;
635                 goto close_dev;
636         }
637         if (writeoption) {
638                 c = write_chunks(fd, offset, dev_size, chunk_buf, chunksize,
639                                  time_st, 0, devname);
640                 if (c < 0 && c != -ENOSPC) {
641                         error = 3;
642                         goto chunk_buf;
643                 }
644                 if (!full) { /* end of device aligned to a block */
645                         offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
646                                   ~(BLOCKSIZE - 1));
647                         c = write_chunks(fd, offset, dev_size, chunk_buf,
648                                          chunksize, time_st, 0, devname);
649                         if (c < 0 && c != -ENOSPC) {
650                                 error = 3;
651                                 goto chunk_buf;
652                         }
653                 }
654                 offset = offset_orig;
655         }
656         if (readoption) {
657                 if (read_chunks(fd, offset, dev_size, chunk_buf, chunksize,
658                                 time_st, 0, devname)) {
659                         error = 2;
660                         goto chunk_buf;
661                 }
662                 if (!full) { /* end of device aligned to a block */
663                         offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
664                                   ~(BLOCKSIZE - 1));
665                         if (read_chunks(fd, offset, dev_size, chunk_buf,
666                                         chunksize, time_st, 0, devname)) {
667                                 error = 2;
668                                 goto chunk_buf;
669                         }
670                 }
671                 if (verbose)
672                         printf("\n%s: data verified successfully\n", progname);
673         }
674         error = error_count;
675 chunk_buf:
676         free(chunk_buf);
677 close_dev:
678         close(fd);
679         return error;
680 }