Whamcloud - gitweb
Branch HEAD
[fs/lustre-release.git] / lustre / utils / llverdev.c
1 /*
2  * Large Block Device Verification Tool.
3  * This program is used to test whether the block device is correctly
4  * handling IO beyond 2TB boundary.
5  * This tool have two working modes
6  * 1. full mode
7  * 2. fast mode
8  *      The full mode is basic mode in which program writes the test pattern
9  * on entire disk. The test pattern (device offset and timestamp) is written
10  * at the beginning of each 4kB block. When the whole device is full then
11  * read operation is performed to verify that the test pattern is correct.
12  *      In the fast mode the program writes data at the critical locations
13  * of the device such as start of the device, before and after multiple of 1GB
14  * offset and at the end.
15  *      A chunk buffer with default size of 1MB is used to write and read test
16  * pattern in bulk.
17  */
18
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
22 #ifndef LUSTRE_UTILS
23 #define LUSTRE_UTILS
24 #endif
25 #ifndef _LARGEFILE64_SOURCE
26 #define _LARGEFILE64_SOURCE
27 #endif
28 #ifndef _FILE_OFFSET_BITS
29 #define _FILE_OFFSET_BITS 64
30 #endif
31
32 #include <features.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <limits.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <getopt.h>
43 #include <time.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/ioctl.h>
47 #include <sys/mount.h>
48 #include <sys/time.h>
49 #include <gnu/stubs.h>
50
51 #ifdef HAVE_EXT2FS_EXT2FS_H
52 #  include <ext2fs/ext2fs.h>
53 #endif
54
55 #define ONE_MB (1024 * 1024)
56 #define ONE_GB (1024 * 1024 * 1024)
57 #define HALF_MB (ONE_MB / 2)
58 #define ONE_KB 1024
59 #define HALF_KB (ONE_KB / 2)
60 #define BLOCKSIZE 4096
61
62 /* Structure for writting test pattern */
63 struct block_data {
64         long long  bd_offset;
65         time_t  bd_time;
66 };
67 static char *progname;          /* name by which this program was run. */
68 static unsigned verbose = 1;    /* prints offset in kB, operation rate */
69 static int readoption;          /* run test in read-only (verify) mode */
70 static int writeoption;         /* run test in write_only mode */
71 const char *devname;            /* name of device to be tested. */
72 static unsigned full = 1;       /* flag to full check */
73 static int fd;
74 static int isatty_flag;
75
76 static struct option const longopts[] =
77 {
78         { "chunksize", required_argument, 0, 'c' },
79         { "force", no_argument, 0, 'f' },
80         { "help", no_argument, 0, 'h' },
81         { "offset", required_argument, 0, 'o' },
82         { "partial", required_argument, 0, 'p' },
83         { "quiet", required_argument, 0, 'q' },
84         { "read", no_argument, 0, 'r' },
85         { "timestamp", required_argument, 0, 't' },
86         { "verbose", no_argument, 0, 'v' },
87         { "write", no_argument, 0, 'w' },
88         { "long", no_argument, 0, 'l' },
89         { 0, 0, 0, 0}
90 };
91
92 /*
93  * Usage: displays help information, whenever user supply --help option in
94  * command or enters incorrect command line.
95  */
96 void usage(int status)
97 {
98         if (status != 0) {
99               printf("\nUsage: %s [OPTION]... <device-name> ...\n",
100                      progname);
101               printf("Block device verification tool.\n"
102                      "\t-t {seconds}, --timestamp, "
103                      "set test time  (default=current time())\n"
104                      "\t-o {offset}, --offset, "
105                      "offset in kB of start of test, default=0\n"
106                      "\t-r, --read run test in verify mode\n"
107                      "\t-w, --write run test in test-pattern mode, default=rw\n"
108                      "\t-v, --verbose\n"
109                      "\t-q, --quiet\n"
110                      "\t-l, --long, full check of device\n"
111                      "\t-p, --partial, for partial check (1GB steps)\n"
112                      "\t-c, --chunksize, IO chunk size, default=1048576\n"
113                      "\t-f, --force, force test to run without confirmation\n"
114                      "\t-h, --help display this help and exit\n");
115         }
116         exit(status);
117 }
118
119 /*
120  * Open_dev: Opens device in specified mode and returns fd.
121  */
122 static int open_dev(const char *devname, int mode)
123 {
124 #ifdef HAVE_EXT2FS_EXT2FS_H
125         int     mount_flags;
126         char    mountpt[80] = "";
127
128         if (ext2fs_check_mount_point(devname, &mount_flags, mountpt,
129                                      sizeof(mountpt))) {
130                 fprintf(stderr, "%s: ext2fs_check_mount_point failed:%s",
131                         progname, strerror(errno));
132                 exit(1);
133         }
134         if (mount_flags & EXT2_MF_MOUNTED){
135                 fprintf(stderr, "%s: %s is already mounted\n", progname,
136                         devname);
137                 exit(1);
138         }
139 #endif
140         fd = open(devname, mode | O_EXCL | O_LARGEFILE);
141         if (fd < 0) {
142                 fprintf(stderr, "%s: Open failed: %s",progname,strerror(errno));
143                 exit(3);
144         }
145         return (fd);
146 }
147
148 #undef HAVE_BLKID_BLKID_H /* sigh, RHEL3 systems do not have libblkid.so.1 */
149 #ifdef HAVE_BLKID_BLKID_H
150 #include <blkid/blkid.h>
151 #endif
152 /*
153  * sizeof_dev: Returns size of device in bytes
154  */
155 static loff_t sizeof_dev(int fd)
156 {
157         loff_t numbytes;
158
159 #ifdef HAVE_BLKID_BLKID_H
160         numbytes = blkid_get_dev_size(fd);
161         if (numbytes <= 0) {
162                 fprintf(stderr, "%s: blkid_get_dev_size(%s) failed",
163                         progname, devname);
164                 return 1;
165         }
166         goto out;
167 #else
168 # if defined BLKGETSIZE64       /* in sys/mount.h */
169         if (ioctl(fd, BLKGETSIZE64, &numbytes) >= 0)
170                 goto out;
171 # endif
172 # if defined BLKGETSIZE         /* in sys/mount.h */
173         {
174                 unsigned long sectors;
175
176                 if (ioctl(fd, BLKGETSIZE, &sectors) >= 0) {
177                         numbytes = (loff_t)sectors << 9;
178                         goto out;
179                 }
180         }
181 # endif
182         {
183                 struct stat statbuf;
184
185                 if (fstat(fd, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) {
186                         numbytes = statbuf.st_size;
187                         goto out;
188                 }
189         }
190         fprintf(stderr, "%s: unable to determine size of %s\n",
191                         progname, devname);
192         return 0;
193 #endif
194
195 out:
196         if (verbose)
197                 printf("%s: %s is %llu bytes (%g GB) in size\n",
198                        progname, devname,
199                        (unsigned long long)numbytes, (double)numbytes / ONE_GB);
200
201         return numbytes;
202 }
203
204 /*
205  * Verify_chunk: Verifies test pattern in each 4kB (BLOCKSIZE) is correct.
206  * Returns 0 if test offset and timestamp is correct otherwise 1.
207  */
208 int verify_chunk(char *chunk_buf, size_t chunksize,
209                  unsigned long long chunk_off, time_t time_st)
210 {
211         struct block_data *bd;
212         char *chunk_end;
213
214         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
215              (char *)chunk_buf < chunk_end;
216              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
217                 bd = (struct block_data *)chunk_buf;
218                 if ((bd->bd_offset == chunk_off) && (bd->bd_time == time_st))
219                         continue;
220
221                 fprintf(stderr, "\n%s: verify failed at offset/timestamp "
222                         "%llu/%lu: found %llu/%lu instead\n", progname,
223                         chunk_off, time_st, bd->bd_offset, bd->bd_time);
224                 return 1;
225         }
226         return 0;
227 }
228
229 /*
230  * fill_chunk: Fills the chunk with current or user specified timestamp
231  * and  offset. The test patters is filled at the beginning of
232  * each 4kB(BLOCKSIZE) blocks in chunk_buf.
233  */
234 void fill_chunk(char *chunk_buf, size_t chunksize, loff_t chunk_off,
235                 time_t time_st)
236 {
237         struct block_data *bd;
238         char *chunk_end;
239
240         for (chunk_end = chunk_buf + chunksize - sizeof(*bd);
241              (char *)chunk_buf < chunk_end;
242              chunk_buf += BLOCKSIZE, chunk_off += BLOCKSIZE) {
243                 bd = (struct block_data *)chunk_buf;
244                 bd->bd_offset = chunk_off;
245                 bd->bd_time = time_st;
246         }
247 }
248
249 void show_rate(char *op, unsigned long long offset, unsigned long long *count)
250 {
251         static time_t last;
252         time_t now;
253         double diff;
254
255         now = time(NULL);
256         diff = now - last;
257
258         if (diff > 4) {
259                 if (last != 0) {
260                         if (isatty_flag)
261                                 printf("\r");
262                         printf("%s offset: %14llukB %5g MB/s            ", op,
263                                offset / ONE_KB, (double)(*count) /ONE_MB /diff);
264                         if (isatty_flag)
265                                 fflush(stdout);
266                         else
267                                 printf("\n");
268
269                         *count = 0;
270                 }
271                 last = now;
272         }
273 }
274
275 /*
276  * write_chunk: write the chunk_buf on the device. The number of write
277  * operations are based on the parameters write_end, offset, and chunksize.
278  */
279 int write_chunks(unsigned long long offset, unsigned long long write_end,
280                  char *chunk_buf, size_t chunksize, time_t time_st)
281 {
282         unsigned long long stride, count = 0;
283
284         stride = full ? chunksize : (ONE_GB - chunksize);
285
286         for (offset = offset & ~(chunksize - 1); offset < write_end;
287              offset += stride) {
288                 if (lseek64(fd, offset, SEEK_SET) == -1) {
289                         fprintf(stderr, "\n%s: lseek64(%llu) failed: %s\n",
290                                 progname, offset, strerror(errno));
291                         return 1;
292                 }
293                 if (offset + chunksize > write_end)
294                         chunksize = write_end - offset;
295
296                 if (!full && offset > chunksize) {
297                         fill_chunk(chunk_buf, chunksize, offset, time_st);
298                         if (write(fd, chunk_buf, chunksize) < 0) {
299                                 fprintf(stderr, "\n%s: write %llu failed: %s\n",
300                                         progname, offset, strerror(errno));
301                                 return 1;
302                         }
303                         offset += chunksize;
304                         if (offset + chunksize > write_end)
305                                 chunksize = write_end - offset;
306                 }
307
308                 fill_chunk(chunk_buf, chunksize, offset, time_st);
309                 if (write(fd, chunk_buf, chunksize) < 0) {
310                         fprintf(stderr, "\n%s: write %llu failed: %s\n",
311                                 progname, offset, strerror(errno));
312                         return 1;
313                 }
314
315                 count += chunksize;
316                 if (verbose > 1)
317                         show_rate("write", offset, &count);
318         }
319         if (verbose > 1) {
320                 show_rate("write", offset, &count);
321                 printf("\nwrite complete\n");
322         }
323         if (fsync(fd) == -1) {
324                 fprintf(stderr, "%s: fsync faild: %s\n", progname,
325                         strerror(errno));
326                         return 1;
327         }
328         return 0;
329 }
330
331 /*
332  * read_chunk: reads the chunk_buf from the device. The number of read
333  * operations are based on the parameters read_end, offset, and chunksize.
334  */
335 int read_chunks(unsigned long long offset, unsigned long long read_end,
336                 char *chunk_buf, size_t chunksize, time_t time_st)
337 {
338         unsigned long long stride, count = 0;
339
340         stride = full ? chunksize : (ONE_GB - chunksize);
341
342         if (ioctl(fd, BLKFLSBUF, 0) < 0 && verbose)
343                 fprintf(stderr, "%s: ioctl BLKFLSBUF failed: %s (ignoring)\n",
344                         progname, strerror(errno));
345
346         for (offset = offset & ~(chunksize - 1); offset < read_end;
347              offset += stride) {
348                 if (lseek64(fd, offset, SEEK_SET) == -1) {
349                         fprintf(stderr, "\n%s: lseek64(%llu) failed: %s\n",
350                                 progname, offset, strerror(errno));
351                         return 1;
352                 }
353                 if (offset + chunksize > read_end)
354                         chunksize = read_end - offset;
355
356                 if (!full && offset > chunksize) {
357                         if (read (fd, chunk_buf, chunksize) < 0) {
358                                 fprintf(stderr, "\n%s: read %llu failed: %s\n",
359                                         progname, offset, strerror(errno));
360                                 return 1;
361                         }
362                         if (verify_chunk(chunk_buf, chunksize, offset,
363                                          time_st) != 0)
364                                 return 1;
365                         offset += chunksize;
366                         if (offset + chunksize >= read_end)
367                                 chunksize = read_end - offset;
368                 }
369
370                 if (read(fd, chunk_buf, chunksize) < 0) {
371                         fprintf(stderr, "\n%s: read failed: %s\n", progname,
372                                 strerror(errno));
373                         return 1;
374                 }
375
376                 if (verify_chunk(chunk_buf, chunksize, offset, time_st) != 0)
377                         return 1;
378
379                 count += chunksize;
380                 if (verbose > 1)
381                         show_rate("read", offset, &count);
382         }
383         if (verbose > 1) {
384                 show_rate("read", offset, &count);
385                 printf("\nread complete\n");
386         }
387         return 0;
388 }
389
390 int main(int argc, char **argv)
391 {
392         time_t time_st = 0;             /* Default timestamp */
393         long long offset = 0, offset_orig; /* offset in kB */
394         size_t chunksize = ONE_MB;      /* IO chunk size */
395         char *chunk_buf = NULL;
396         unsigned int force = 0;         /* run test run without confirmation*/
397         unsigned long long dev_size = 0;
398         char yesno[4];
399         int mode = O_RDWR;              /* mode which device should be opened */
400         int error = 0, c;
401
402         progname = strrchr(argv[0], '/') == NULL ?
403                 argv[0] : strrchr(argv[0], '/') + 1;
404         while ((c = getopt_long(argc, argv, "c:fhlo:pqrt:vw", longopts,
405                                 NULL)) != -1) {
406                 switch (c) {
407                 case 'c':
408                         chunksize = (strtoul(optarg, NULL, 0) * ONE_MB);
409                         if (!chunksize) {
410                                 fprintf(stderr, "%s: chunk size value should be"
411                                         "nonzero and multiple of 1MB\n",
412                                         progname);
413                                 return -1;
414                         }
415                         break;
416                 case 'f':
417                         force = 1;
418                         break;
419                 case 'l':
420                         full = 1;
421                         break;
422                 case 'o':
423                         offset = strtoull(optarg, NULL, 0) * ONE_KB;
424                         break;
425                 case 'p':
426                         full = 0;
427                         break;
428                 case 'q':
429                         verbose = 0;
430                         break;
431                 case 'r':
432                         readoption = 1;
433                         mode = O_RDONLY;
434                         break;
435                 case 't':
436                         time_st = (time_t)strtoul(optarg, NULL, 0);
437                         break;
438                 case 'v':
439                         verbose++;
440                         break;
441                 case 'w':
442                         writeoption = 1;
443                         mode = O_WRONLY;
444                         break;
445                 case 'h':
446                 default:
447                         usage (1);
448                         return 0;
449                 }
450         }
451         offset_orig = offset;
452         devname = argv[optind];
453         if (!devname) {
454                 fprintf(stderr, "%s: device name not given\n", progname);
455                 usage (1);
456                 return -1;
457         }
458
459         if (readoption && writeoption)
460                 mode = O_RDWR;
461         if (!readoption && !writeoption) {
462                 readoption = 1;
463                 writeoption = 1;
464         }
465
466         if (!force && writeoption) {
467                 printf("%s: permanently overwrite all data on %s (yes/no)? ",
468                        progname, devname);
469                 scanf("%3s", yesno);
470                 if (!(strcasecmp("yes", yesno) || strcasecmp("y", yesno))) {
471                         printf("Not continuing due to '%s' response", yesno);
472                         return 0;
473                 }
474         }
475
476         if (!writeoption && time_st == 0) {
477                 fprintf(stderr, "%s: must give timestamp for read-only test\n",
478                         progname);
479                 usage(1);
480         }
481
482         fd = open_dev(devname, mode);
483         dev_size = sizeof_dev(fd);
484         if (!dev_size) {
485                 fprintf(stderr, "%s: cannot test on device size < 1MB\n",
486                         progname);
487                 error = 7;
488                 goto close_dev;
489         }
490
491         if (dev_size < (offset * 2)) {
492                 fprintf(stderr, "%s: device size %llu < offset %llu\n",
493                         progname, dev_size, offset);
494                 error = 6;
495                 goto close_dev;
496         }
497         if (!time_st)
498                 (void)time(&time_st);
499
500         isatty_flag = isatty(STDOUT_FILENO);
501
502         if (verbose)
503                 printf("Timestamp: %lu\n", time_st);
504
505         chunk_buf = (char *)calloc(chunksize, 1);
506         if (chunk_buf == NULL) {
507                 fprintf(stderr, "%s: memory allocation failed for chunk_buf\n",
508                         progname);
509                 error = 4;
510                 goto close_dev;
511         }
512         if (writeoption) {
513                 if (write_chunks(offset, dev_size, chunk_buf, chunksize,
514                                  time_st)) {
515                         error = 3;
516                         goto chunk_buf;
517                 }
518                 if (!full) {  /* end of device aligned to a block */
519                         offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
520                                   ~(BLOCKSIZE - 1));
521                         if (write_chunks(offset, dev_size, chunk_buf, chunksize,
522                                          time_st)) {
523                                 error = 3;
524                                 goto chunk_buf;
525                         }
526                 }
527                 offset = offset_orig;
528         }
529         if (readoption) {
530                 if (read_chunks(offset, dev_size, chunk_buf, chunksize,
531                                 time_st)) {
532                         error = 2;
533                         goto chunk_buf;
534                 }
535                 if (!full) { /* end of device aligned to a block */
536                         offset = ((dev_size - chunksize + BLOCKSIZE - 1) &
537                                   ~(BLOCKSIZE - 1));
538                         if (read_chunks(offset, dev_size, chunk_buf, chunksize,
539                                         time_st)) {
540                                 error = 2;
541                                 goto chunk_buf;
542                         }
543                 }
544                 if (verbose)
545                         printf("\n%s: data verified successfully\n", progname);
546         }
547         error = 0;
548 chunk_buf:
549         free(chunk_buf);
550 close_dev:
551         close(fd);
552         return error;
553 }