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