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