Whamcloud - gitweb
LU-17662 osd-zfs: Support for ZFS 2.2.3
[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 static 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 static int verify_chunk(char *chunk_buf, const size_t chunksize,
226                         unsigned long long chunk_off,
227                         const unsigned long long time_st,
228                         const unsigned long long inode_st,
229                         const char *file)
230 {
231         struct block_data *bd;
232         char *chunk_end;
233
234         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
235              (char *)chunk_buf < chunk_end;
236              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
237                 bd = (struct block_data *)chunk_buf;
238                 if ((bd->bd_offset == chunk_off) && (bd->bd_time == time_st) &&
239                     (bd->bd_inode == inode_st))
240                         continue;
241
242                 fprintf(stderr,
243                         "\n%s: verify %s failed offset/timestamp/inode %llu/%llu/%llu: found %llu/%llu/%llu instead\n",
244                         progname, file, chunk_off, time_st, inode_st,
245                         bd->bd_offset, bd->bd_time, bd->bd_inode);
246                 error_count++;
247                 return 1;
248         }
249         return 0;
250 }
251
252 /*
253  * fill_chunk: Fills the chunk with current or user specified timestamp
254  * and offset. The test pattern is filled at the beginning of
255  * each 4kB(BLOCKSIZE) blocks in chunk_buf.
256  */
257 static void fill_chunk(char *chunk_buf, size_t chunksize, loff_t chunk_off,
258                        const time_t time_st, const ino_t inode_st)
259 {
260         struct block_data *bd;
261         char *chunk_end;
262
263         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
264              (char *)chunk_buf < chunk_end;
265              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
266                 bd = (struct block_data *)chunk_buf;
267                 bd->bd_offset = chunk_off;
268                 bd->bd_time = time_st;
269                 bd->bd_inode = inode_st;
270         }
271 }
272
273 static void show_rate(char *op, unsigned long long offset,
274                       unsigned long long count)
275 {
276         static unsigned long long subtot, total;
277         static time_t start, last;
278         time_t now;
279         double diff;
280
281         now = time(NULL);
282         if (!start)
283                 start = now;
284         if (count == 0) {
285                 subtot = total;
286                 last = start;
287         } else {
288                 subtot += count;
289                 total += count;
290         }
291         diff = now - last;
292
293         if ((verbose > 1 && diff >= (verbose > 3 ? 1 : 5)) || count == 0) {
294                 if (last != 0) {
295                         if (isatty_flag)
296                                 printf("\r");
297                         printf("%s offset: %14llukB %5g MB/s            ", op,
298                                offset / ONE_KB, (double)subtot / ONE_MB / diff);
299                         if (isatty_flag && count)
300                                 fflush(stdout);
301                         else
302                                 printf("\n");
303
304                         subtot = 0;
305                 }
306                 if (count == 0)
307                         subtot = total = start = last = 0;
308                 else
309                         last = now;
310         }
311 }
312
313 /*
314  * Write a chunk to disk, handling errors, interrupted writes, etc.
315  *
316  * If there is an IO error hit during the write, it is possible that
317  * this will just show up as a short write, and a subsequent write
318  * will return the actual error.  We want to continue in the face of
319  * minor media errors so that we can validate the whole device if
320  * possible, but if there are many errors we don't want to loop forever.
321  *
322  * The error count will be returned upon exit to ensure that the
323  * media errors are detected even if nobody is looking at the output.
324  *
325  * Returns 0 on success, or -ve errno on failure.
326  */
327 static size_t write_retry(int fd, const char *chunk_buf, size_t nrequested,
328                           unsigned long long offset, const char *file)
329 {
330         long nwritten;
331
332 retry:
333         nwritten = pwrite(fd, chunk_buf, nrequested, offset);
334         if (nwritten < 0) {
335                 if (errno != ENOSPC) {
336                         fprintf(stderr, "\n%s: write %s@%llu+%zi failed: %s\n",
337                                 progname, file, offset, nrequested,
338                                 strerror(errno));
339                         if (error_count++ < MAX_ALLOWED_IO_ERR)
340                                 return 0;
341                 }
342                 return -errno;
343         }
344         if (nwritten < nrequested) {
345                 fprintf(stderr, "\n%s: write %s@%llu+%zi short: %ld/%ld\n",
346                         progname, file, offset, nrequested, nwritten,
347                         nrequested);
348                 offset += nwritten;
349                 chunk_buf += nwritten;
350                 nrequested -= nwritten;
351                 goto retry;
352         }
353
354         return 0;
355 }
356
357 /*
358  * write_chunks: write the chunk_buf on the device. The number of write
359  * operations are based on the parameters write_end, offset, and chunksize.
360  *
361  * Returns 0 on success, or -ve error number on failure.
362  */
363 static int write_chunks(int fd, unsigned long long offset,
364                         unsigned long long *write_end, char *chunk_buf,
365                         size_t chunksize, const time_t time_st,
366                         const ino_t inode_st, const char *file)
367 {
368         unsigned long long stride;
369
370         stride = full ? chunksize : ONE_GB;
371         for (offset = offset & ~(chunksize - 1); offset < *write_end;
372              offset += stride) {
373                 int ret;
374
375                 if (offset + chunksize > *write_end)
376                         chunksize = *write_end - offset;
377                 fill_chunk(chunk_buf, chunksize, offset, time_st, inode_st);
378                 ret = write_retry(fd, chunk_buf, chunksize, offset, file);
379                 if (ret < 0) {
380                         if (ret == -ENOSPC)
381                                 *write_end = offset - stride;
382                         return ret;
383                 }
384
385                 show_rate("write", offset, chunksize);
386         }
387
388         if (fsync(fd) == -1) {
389                 fprintf(stderr, "%s: fsync failed: %s\n", progname,
390                         strerror(errno));
391                 return -errno;
392         }
393         return 0;
394 }
395
396 /*
397  * read_chunk: reads the chunk_buf from the device. The number of read
398  * operations are based on the parameters read_end, offset, and chunksize.
399  */
400 static int read_chunks(int fd, unsigned long long offset,
401                        unsigned long long read_end, char *chunk_buf,
402                        size_t chunksize, const time_t time_st,
403                        const ino_t inode_st, const char *file)
404 {
405         unsigned long long stride;
406
407         if (ioctl(fd, BLKFLSBUF, 0) < 0 && verbose > 1)
408                 fprintf(stderr, "%s: ioctl BLKFLSBUF failed: %s (ignoring)\n",
409                         progname, strerror(errno));
410
411         stride = full ? chunksize : ONE_GB;
412         for (offset = offset & ~(chunksize - 1); offset < read_end;
413              offset += stride) {
414                 ssize_t nread, rc;
415
416                 if (offset + chunksize > read_end)
417                         chunksize = read_end - offset;
418
419                 nread = 0;
420                 /* reset errno before calling pread */
421                 errno = 0;
422 read_more:
423                 rc = pread(fd, chunk_buf + nread, chunksize - nread,
424                            offset + nread);
425                 if (rc == 0) {
426                         fprintf(stderr, "\n%s: read %s@%llu+%zi no data: %s\n",
427                                 progname, file, offset + nread,
428                                 chunksize - nread, strerror(errno));
429                         break;
430                 }
431                 if (rc < 0) {
432                         fprintf(stderr, "\n%s: read %s@%llu+%zi failed: %s\n",
433                                 progname, file, offset + nread,
434                                 chunksize - nread, strerror(errno));
435                         if (error_count++ < MAX_ALLOWED_IO_ERR)
436                                 continue;
437                         return -errno;
438                 }
439                 nread += rc;
440                 if (nread < chunksize) {
441                         fprintf(stderr,
442                                 "\n%s: read %s@%llu+%zi short: %zi/%zi\n",
443                                 progname, file, offset + nread,
444                                 chunksize - nread, rc, chunksize);
445                         goto read_more;
446                 }
447
448                 if (verify_chunk(chunk_buf, chunksize, offset, time_st,
449                                  inode_st, file) != 0)
450                         return 1;
451
452                 show_rate("read", offset, chunksize);
453         }
454         return 0;
455 }
456
457 static int parse_size(const char *optarg, unsigned long long *size,
458                       unsigned long long size_units)
459 {
460         char *end;
461
462         *size = strtoull(optarg, &end, 0);
463         if (*end != '\0') {
464                 size_units = 1;
465                 switch (*end) {
466                 case 'e': case 'E': size_units <<= 10; /* fallthrough */
467                 case 'p': case 'P': size_units <<= 10; /* fallthrough */
468                 case 't': case 'T': size_units <<= 10; /* fallthrough */
469                 case 'g': case 'G': size_units <<= 10; /* fallthrough */
470                 case 'm': case 'M': size_units <<= 10; /* fallthrough */
471                 case 'k': case 'K': size_units <<= 10; break;
472                 default:
473                         return -1;
474                 }
475         }
476         *size *= size_units;
477
478         return 0;
479 }
480
481 int main(int argc, char **argv)
482 {
483         unsigned long long chunksize = ONE_MB; /* IO chunk size */
484         unsigned long long offset = 0; /* offset in kB */
485         unsigned long long dev_size = 0;
486         unsigned int force = 0; /* run test run without confirmation*/
487         time_t time_st = 0; /* Default timestamp */
488         char *chunk_buf = NULL;
489         char yesno[4];
490         int mode = O_RDWR; /* mode which device should be opened */
491         int error = 0, c;
492         int fd;
493
494         progname = !strrchr(argv[0], '/') ? argv[0] : strrchr(argv[0], '/') + 1;
495         while ((c = getopt_long(argc, argv, "c:fhlo:pqrs:t:vw", long_opts,
496                                 NULL)) != -1) {
497                 switch (c) {
498                 case 'c':
499                         if (parse_size(optarg, &chunksize, ONE_MB)) {
500                                 fprintf(stderr, "%s: bad chunksize '%s'\n",
501                                         progname, optarg);
502                                 return -1;
503                         }
504                         if (chunksize > ONE_GB || chunksize < 4096) {
505                                 fprintf(stderr,
506                                         "%s: valid chunksize 4KB-1GB, not '%s'\n",
507                                         progname, optarg);
508                                 return -1;
509                         }
510                         break;
511                 case 'f':
512                         force = 1;
513                         break;
514                 case 'l':
515                         full = 1;
516                         break;
517                 case 'o':
518                         if (parse_size(optarg, &offset, ONE_KB)) {
519                                 fprintf(stderr, "%s: bad offset '%s'\n",
520                                         progname, optarg);
521                                 return -1;
522                         }
523                         break;
524                 case 'p':
525                         full = 0;
526                         break;
527                 case 'q':
528                         verbose = 0;
529                         break;
530                 case 'r':
531                         readoption = 1;
532                         mode = O_RDONLY;
533                         break;
534                 case 's':
535                         if (parse_size(optarg, &dev_size, ONE_MB)) {
536                                 fprintf(stderr, "%s: bad size '%s'\n",
537                                         progname, optarg);
538                                 return -1;
539                         }
540                         break;
541                 case 't':
542                         time_st = (time_t)strtoul(optarg, NULL, 0);
543                         break;
544                 case 'v':
545                         verbose++;
546                         break;
547                 case 'w':
548                         writeoption = 1;
549                         mode = O_WRONLY;
550                         break;
551                 case 'h':
552                 default:
553                         usage(1);
554                         return 0;
555                 }
556         }
557         devname = argv[optind];
558         if (!devname) {
559                 fprintf(stderr, "%s: device name not given\n", progname);
560                 usage(1);
561                 return -1;
562         }
563
564         if (readoption && writeoption)
565                 mode = O_RDWR;
566         if (!readoption && !writeoption) {
567                 readoption = 1;
568                 writeoption = 1;
569         }
570
571         if (!force && writeoption) {
572                 printf("%s: permanently overwrite all data on %s (yes/no)? ",
573                        progname, devname);
574                 if (scanf("%3s", yesno) == EOF && ferror(stdin)) {
575                         perror("reading from stdin");
576                         return -1;
577                 }
578                 if (!(strcasecmp("yes", yesno) || strcasecmp("y", yesno))) {
579                         printf("Not continuing due to '%s' response", yesno);
580                         return 0;
581                 }
582         }
583
584         if (!writeoption && time_st == 0) {
585                 fprintf(stderr, "%s: must give timestamp for read-only test\n",
586                         progname);
587                 usage(1);
588         }
589
590         fd = open_dev(devname, mode);
591         dev_size = sizeof_dev(fd, dev_size);
592         if (verbose)
593                 printf("%s: %s is %llu bytes (%g GB) in size\n",
594                        progname, devname, dev_size, (double)dev_size / ONE_GB);
595
596         if (!dev_size) {
597                 fprintf(stderr, "%s: cannot test on device size < 1MB\n",
598                         progname);
599                 error = 7;
600                 goto close_dev;
601         }
602
603         if (offset >= dev_size - chunksize) {
604                 fprintf(stderr, "%s: offset %llu >= device size %llu\n",
605                         progname, offset, dev_size);
606                 error = 6;
607                 goto close_dev;
608         }
609         if (!time_st)
610                 (void)time(&time_st);
611
612         isatty_flag = isatty(STDOUT_FILENO);
613
614         if (verbose)
615                 printf("timestamp: %lu chunksize: %llu size: %llu\n",
616                        time_st, chunksize, dev_size);
617
618         chunk_buf = (char *)calloc(chunksize, 1);
619         if (!chunk_buf) {
620                 fprintf(stderr, "%s: memory allocation failed for chunk_buf\n",
621                         progname);
622                 error = 4;
623                 goto close_dev;
624         }
625         if (writeoption) {
626                 c = write_chunks(fd, offset, &dev_size, chunk_buf, chunksize,
627                                  time_st, ino_st, devname);
628                 if (c < 0 && c != -ENOSPC) {
629                         error = 3;
630                         goto chunk_buf;
631                 }
632                 if (!full && c != -ENOSPC) { /* end of device block-aligned */
633                         offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
634                                   ~(BLOCKSIZE - 1));
635                         c = write_chunks(fd, offset, &dev_size, chunk_buf,
636                                          chunksize, time_st, ino_st, devname);
637                         if (c < 0 && c != -ENOSPC) {
638                                 error = 3;
639                                 goto chunk_buf;
640                         }
641                 }
642                 show_rate("write", dev_size, 0);
643                 if (verbose > 1)
644                         printf("write complete\n");
645         }
646         if (readoption) {
647                 if (read_chunks(fd, offset, dev_size, chunk_buf, chunksize,
648                                 time_st, ino_st, devname)) {
649                         error = 2;
650                         goto chunk_buf;
651                 }
652                 if (!full && c != -ENOSPC) { /* end of device block-aligned */
653                         offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
654                                   ~(BLOCKSIZE - 1));
655                         if (read_chunks(fd, offset, dev_size, chunk_buf,
656                                         chunksize, time_st, ino_st, devname)) {
657                                 error = 2;
658                                 goto chunk_buf;
659                         }
660                 }
661                 show_rate("read", dev_size, 0);
662                 if (verbose > 1)
663                         printf("read complete\n");
664                 if (verbose)
665                         printf("%s: data verified successfully\n", progname);
666         }
667         error = error_count;
668 chunk_buf:
669         free(chunk_buf);
670 close_dev:
671         close(fd);
672         return error;
673 }