Whamcloud - gitweb
b=19720
[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  2008 Sun Microsystems, Inc. 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. fast mode
44  *      The full mode is basic mode in which program writes the test pattern
45  * on entire disk. The test pattern (device offset and timestamp) is written
46  * at the beginning of each 4kB block. When the whole device is full then
47  * read operation is performed to verify that the test pattern is correct.
48  *      In the fast mode the program writes data at the critical locations
49  * of the device such as start of the device, before and after multiple of 1GB
50  * offset and at the end.
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         long long  bd_offset;
101         time_t  bd_time;
102 };
103 static char *progname;          /* name by which this program was run. */
104 static unsigned verbose = 1;    /* prints offset in kB, operation rate */
105 static int readoption;          /* run test in read-only (verify) mode */
106 static int writeoption;         /* run test in write_only mode */
107 const char *devname;            /* name of device to be tested. */
108 static unsigned full = 1;       /* flag to full check */
109 static int fd;
110 static int isatty_flag;
111
112 static struct option const longopts[] =
113 {
114         { "chunksize", required_argument, 0, 'c' },
115         { "force", no_argument, 0, 'f' },
116         { "help", no_argument, 0, 'h' },
117         { "offset", required_argument, 0, 'o' },
118         { "partial", required_argument, 0, 'p' },
119         { "quiet", required_argument, 0, 'q' },
120         { "read", no_argument, 0, 'r' },
121         { "timestamp", required_argument, 0, 't' },
122         { "verbose", no_argument, 0, 'v' },
123         { "write", no_argument, 0, 'w' },
124         { "long", no_argument, 0, 'l' },
125         { 0, 0, 0, 0}
126 };
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 test in verify mode\n"
143                      "\t-w, --write run test 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, --chunksize, IO chunk 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 #ifdef HAVE_EXT2FS_EXT2FS_H
161         int     mount_flags;
162         char    mountpt[80] = "";
163
164         if (ext2fs_check_mount_point(devname, &mount_flags, mountpt,
165                                      sizeof(mountpt))) {
166                 fprintf(stderr, "%s: ext2fs_check_mount_point failed:%s",
167                         progname, strerror(errno));
168                 exit(1);
169         }
170         if (mount_flags & EXT2_MF_MOUNTED){
171                 fprintf(stderr, "%s: %s is already mounted\n", progname,
172                         devname);
173                 exit(1);
174         }
175 #endif
176         fd = open(devname, mode | O_EXCL | O_LARGEFILE);
177         if (fd < 0) {
178                 fprintf(stderr, "%s: Open failed: %s",progname,strerror(errno));
179                 exit(3);
180         }
181         return (fd);
182 }
183
184 #undef HAVE_BLKID_BLKID_H /* sigh, RHEL3 systems do not have libblkid.so.1 */
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, size_t chunksize,
245                  unsigned long long chunk_off, time_t time_st)
246 {
247         struct block_data *bd;
248         char *chunk_end;
249
250         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
251              (char *)chunk_buf < chunk_end;
252              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
253                 bd = (struct block_data *)chunk_buf;
254                 if ((bd->bd_offset == chunk_off) && (bd->bd_time == time_st))
255                         continue;
256
257                 fprintf(stderr, "\n%s: verify failed at offset/timestamp "
258                         "%llu/%lu: found %llu/%lu instead\n", progname,
259                         chunk_off, time_st, bd->bd_offset, bd->bd_time);
260                 return 1;
261         }
262         return 0;
263 }
264
265 /*
266  * fill_chunk: Fills the chunk with current or user specified timestamp
267  * and  offset. The test patters is filled at the beginning of
268  * each 4kB(BLOCKSIZE) blocks in chunk_buf.
269  */
270 void fill_chunk(char *chunk_buf, size_t chunksize, loff_t chunk_off,
271                 time_t time_st)
272 {
273         struct block_data *bd;
274         char *chunk_end;
275
276         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
277              (char *)chunk_buf < chunk_end;
278              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
279                 bd = (struct block_data *)chunk_buf;
280                 bd->bd_offset = chunk_off;
281                 bd->bd_time = time_st;
282         }
283 }
284
285 void show_rate(char *op, unsigned long long offset, unsigned long long *count)
286 {
287         static time_t last;
288         time_t now;
289         double diff;
290
291         now = time(NULL);
292         diff = now - last;
293
294         if (diff > 4) {
295                 if (last != 0) {
296                         if (isatty_flag)
297                                 printf("\r");
298                         printf("%s offset: %14llukB %5g MB/s            ", op,
299                                offset / ONE_KB, (double)(*count) /ONE_MB /diff);
300                         if (isatty_flag)
301                                 fflush(stdout);
302                         else
303                                 printf("\n");
304
305                         *count = 0;
306                 }
307                 last = now;
308         }
309 }
310
311 /*
312  * write_chunk: write the chunk_buf on the device. The number of write
313  * operations are based on the parameters write_end, offset, and chunksize.
314  */
315 int write_chunks(unsigned long long offset, unsigned long long write_end,
316                  char *chunk_buf, size_t chunksize, time_t time_st)
317 {
318         unsigned long long stride, count = 0;
319
320         stride = full ? chunksize : (ONE_GB - chunksize);
321
322         for (offset = offset & ~(chunksize - 1); offset < write_end;
323              offset += stride) {
324                 if (lseek64(fd, offset, SEEK_SET) == -1) {
325                         fprintf(stderr, "\n%s: lseek64(%llu) failed: %s\n",
326                                 progname, offset, strerror(errno));
327                         return 1;
328                 }
329                 if (offset + chunksize > write_end)
330                         chunksize = write_end - offset;
331
332                 if (!full && offset > chunksize) {
333                         fill_chunk(chunk_buf, chunksize, offset, time_st);
334                         if (write(fd, chunk_buf, chunksize) < 0) {
335                                 fprintf(stderr, "\n%s: write %llu failed: %s\n",
336                                         progname, offset, strerror(errno));
337                                 return 1;
338                         }
339                         offset += chunksize;
340                         if (offset + chunksize > write_end)
341                                 chunksize = write_end - offset;
342                 }
343
344                 fill_chunk(chunk_buf, chunksize, offset, time_st);
345                 if (write(fd, chunk_buf, chunksize) < 0) {
346                         fprintf(stderr, "\n%s: write %llu failed: %s\n",
347                                 progname, offset, strerror(errno));
348                         return 1;
349                 }
350
351                 count += chunksize;
352                 if (verbose > 1)
353                         show_rate("write", offset, &count);
354         }
355         if (verbose > 1) {
356                 show_rate("write", offset, &count);
357                 printf("\nwrite complete\n");
358         }
359         if (fsync(fd) == -1) {
360                 fprintf(stderr, "%s: fsync faild: %s\n", progname,
361                         strerror(errno));
362                         return 1;
363         }
364         return 0;
365 }
366
367 /*
368  * read_chunk: reads the chunk_buf from the device. The number of read
369  * operations are based on the parameters read_end, offset, and chunksize.
370  */
371 int read_chunks(unsigned long long offset, unsigned long long read_end,
372                 char *chunk_buf, size_t chunksize, time_t time_st)
373 {
374         unsigned long long stride, count = 0;
375
376         stride = full ? chunksize : (ONE_GB - chunksize);
377
378         if (ioctl(fd, BLKFLSBUF, 0) < 0 && verbose)
379                 fprintf(stderr, "%s: ioctl BLKFLSBUF failed: %s (ignoring)\n",
380                         progname, strerror(errno));
381
382         for (offset = offset & ~(chunksize - 1); offset < read_end;
383              offset += stride) {
384                 if (lseek64(fd, offset, SEEK_SET) == -1) {
385                         fprintf(stderr, "\n%s: lseek64(%llu) failed: %s\n",
386                                 progname, offset, strerror(errno));
387                         return 1;
388                 }
389                 if (offset + chunksize > read_end)
390                         chunksize = read_end - offset;
391
392                 if (!full && offset > chunksize) {
393                         if (read (fd, chunk_buf, chunksize) < 0) {
394                                 fprintf(stderr, "\n%s: read %llu failed: %s\n",
395                                         progname, offset, strerror(errno));
396                                 return 1;
397                         }
398                         if (verify_chunk(chunk_buf, chunksize, offset,
399                                          time_st) != 0)
400                                 return 1;
401                         offset += chunksize;
402                         if (offset + chunksize >= read_end)
403                                 chunksize = read_end - offset;
404                 }
405
406                 if (read(fd, chunk_buf, chunksize) < 0) {
407                         fprintf(stderr, "\n%s: read failed: %s\n", progname,
408                                 strerror(errno));
409                         return 1;
410                 }
411
412                 if (verify_chunk(chunk_buf, chunksize, offset, time_st) != 0)
413                         return 1;
414
415                 count += chunksize;
416                 if (verbose > 1)
417                         show_rate("read", offset, &count);
418         }
419         if (verbose > 1) {
420                 show_rate("read", offset, &count);
421                 printf("\nread complete\n");
422         }
423         return 0;
424 }
425
426 int main(int argc, char **argv)
427 {
428         time_t time_st = 0;             /* Default timestamp */
429         long long offset = 0, offset_orig; /* offset in kB */
430         size_t chunksize = ONE_MB;      /* IO chunk size */
431         char *chunk_buf = NULL;
432         unsigned int force = 0;         /* run test run without confirmation*/
433         unsigned long long dev_size = 0;
434         char yesno[4];
435         int mode = O_RDWR;              /* mode which device should be opened */
436         int error = 0, c;
437
438         progname = strrchr(argv[0], '/') == NULL ?
439                 argv[0] : strrchr(argv[0], '/') + 1;
440         while ((c = getopt_long(argc, argv, "c:fhlo:pqrt:vw", longopts,
441                                 NULL)) != -1) {
442                 switch (c) {
443                 case 'c':
444                         chunksize = (strtoul(optarg, NULL, 0) * ONE_MB);
445                         if (!chunksize) {
446                                 fprintf(stderr, "%s: chunk size value should be"
447                                         "nonzero and multiple of 1MB\n",
448                                         progname);
449                                 return -1;
450                         }
451                         break;
452                 case 'f':
453                         force = 1;
454                         break;
455                 case 'l':
456                         full = 1;
457                         break;
458                 case 'o':
459                         offset = strtoull(optarg, NULL, 0) * ONE_KB;
460                         break;
461                 case 'p':
462                         full = 0;
463                         break;
464                 case 'q':
465                         verbose = 0;
466                         break;
467                 case 'r':
468                         readoption = 1;
469                         mode = O_RDONLY;
470                         break;
471                 case 't':
472                         time_st = (time_t)strtoul(optarg, NULL, 0);
473                         break;
474                 case 'v':
475                         verbose++;
476                         break;
477                 case 'w':
478                         writeoption = 1;
479                         mode = O_WRONLY;
480                         break;
481                 case 'h':
482                 default:
483                         usage (1);
484                         return 0;
485                 }
486         }
487         offset_orig = offset;
488         devname = argv[optind];
489         if (!devname) {
490                 fprintf(stderr, "%s: device name not given\n", progname);
491                 usage (1);
492                 return -1;
493         }
494
495         if (readoption && writeoption)
496                 mode = O_RDWR;
497         if (!readoption && !writeoption) {
498                 readoption = 1;
499                 writeoption = 1;
500         }
501
502         if (!force && writeoption) {
503                 printf("%s: permanently overwrite all data on %s (yes/no)? ",
504                        progname, devname);
505                 if (scanf("%3s", yesno) == EOF && ferror(stdin)) {
506                         perror("reading from stdin");
507                         return -1;
508                 }
509                 if (!(strcasecmp("yes", yesno) || strcasecmp("y", yesno))) {
510                         printf("Not continuing due to '%s' response", yesno);
511                         return 0;
512                 }
513         }
514
515         if (!writeoption && time_st == 0) {
516                 fprintf(stderr, "%s: must give timestamp for read-only test\n",
517                         progname);
518                 usage(1);
519         }
520
521         fd = open_dev(devname, mode);
522         dev_size = sizeof_dev(fd);
523         if (!dev_size) {
524                 fprintf(stderr, "%s: cannot test on device size < 1MB\n",
525                         progname);
526                 error = 7;
527                 goto close_dev;
528         }
529
530         if (dev_size < (offset * 2)) {
531                 fprintf(stderr, "%s: device size %llu < offset %llu\n",
532                         progname, dev_size, offset);
533                 error = 6;
534                 goto close_dev;
535         }
536         if (!time_st)
537                 (void)time(&time_st);
538
539         isatty_flag = isatty(STDOUT_FILENO);
540
541         if (verbose)
542                 printf("Timestamp: %lu\n", time_st);
543
544         chunk_buf = (char *)calloc(chunksize, 1);
545         if (chunk_buf == NULL) {
546                 fprintf(stderr, "%s: memory allocation failed for chunk_buf\n",
547                         progname);
548                 error = 4;
549                 goto close_dev;
550         }
551         if (writeoption) {
552                 if (write_chunks(offset, dev_size, chunk_buf, chunksize,
553                                  time_st)) {
554                         error = 3;
555                         goto chunk_buf;
556                 }
557                 if (!full) {  /* end of device aligned to a block */
558                         offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
559                                   ~(BLOCKSIZE - 1));
560                         if (write_chunks(offset, dev_size, chunk_buf, chunksize,
561                                          time_st)) {
562                                 error = 3;
563                                 goto chunk_buf;
564                         }
565                 }
566                 offset = offset_orig;
567         }
568         if (readoption) {
569                 if (read_chunks(offset, dev_size, chunk_buf, chunksize,
570                                 time_st)) {
571                         error = 2;
572                         goto chunk_buf;
573                 }
574                 if (!full) { /* end of device aligned to a block */
575                         offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
576                                   ~(BLOCKSIZE - 1));
577                         if (read_chunks(offset, dev_size, chunk_buf, chunksize,
578                                         time_st)) {
579                                 error = 2;
580                                 goto chunk_buf;
581                         }
582                 }
583                 if (verbose)
584                         printf("\n%s: data verified successfully\n", progname);
585         }
586         error = 0;
587 chunk_buf:
588         free(chunk_buf);
589 close_dev:
590         close(fd);
591         return error;
592 }