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