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