[PATCH] Add drivescan tool
[disktools.git] / drivescan.c
1 /*
2  * Copyright (C) 2004 Jens Axboe <axboe@suse.de>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include <signal.h>
13 #include <string.h>
14 #include <getopt.h>
15 #include <errno.h>
16 #include <sys/types.h>
17 #include <sys/ioctl.h>
18 #include <sys/time.h>
19 #include <sys/stat.h>
20
21 enum {
22         arch_x86_64,
23         arch_i386,
24         arch_ppc,
25         arch_ia64,
26 };
27
28 #if defined(__x86_64)
29 #define BITS_PER_LONG 64
30 static int arch = arch_x86_64;
31 inline unsigned long ffz(unsigned long bitmask)
32 {
33         __asm__("bsfq %1,%0" :"=r" (bitmask) :"r" (~bitmask));
34         return bitmask;
35 }
36 #elif defined (__i386__)
37 #define BITS_PER_LONG 32
38 static int arch = arch_i386;
39 inline unsigned long ffz(unsigned long bitmask)
40 {
41         __asm__("bsfl %1,%0" :"=r" (bitmask) :"r" (~bitmask));
42         return bitmask;
43 }
44 #elif defined (__powerpc__)
45 #define BITS_PER_LONG 32
46 static int arch = arch_ppc;
47 inline int __ilog2(unsigned long bitmask)
48 {
49         int lz;
50
51         asm ("cntlzw %0,%1" : "=r" (lz) : "r" (bitmask));
52         return 31 - lz;
53 }
54
55 static inline int ffz(unsigned long bitmask)
56 {
57         if ((bitmask = ~bitmask) == 0)
58                 return 32;
59         return  __ilog2(bitmask & -bitmask);
60 }
61 #elif defined(__ia64__)
62 #define BITS_PER_LONG 64
63 static int arch = arch_ia64;
64 #define ia64_popcnt(x)                                                  \
65 ({                                                                      \
66         unsigned long ia64_intri_res;                                   \
67         asm ("popcnt %0=%1" : "=r" (ia64_intri_res) : "r" (x));         \
68         ia64_intri_res;                                                 \
69 })
70 inline unsigned long ffz(unsigned long bitmask)
71 {
72         return ia64_popcnt(bitmask & (~bitmask - 1));
73 }
74 #else
75 #error "Unsupported architecture"
76 #endif
77
78 #if BITS_PER_LONG == 32
79 typedef unsigned long long u64;
80 typedef unsigned long u32;
81 #elif BITS_PER_LONG == 64
82 typedef unsigned long u64;
83 typedef unsigned int u32;
84 #endif
85
86 #ifndef BLKGETSIZE64
87 #define BLKGETSIZE64 _IOR(0x12,114,size_t)
88 #endif
89
90 #define BS      (4096)
91 #define MASK    (4095)
92
93 #define ALIGN(buf)      (((unsigned long) (buf) + MASK) & ~(MASK))
94
95 #define BITMAP_BLOCKS(blocks)   (((blocks) / BITS_PER_LONG) + 1)
96 #define BITMAP_SIZE(blocks)     (BITMAP_BLOCKS(blocks) * sizeof(unsigned long))
97
98 #define RANDOM_COVERAGE         (90)
99
100 static char device[256];
101 static char state_file[256];
102 static int progress_interval;
103 static int block_size;
104 static int restore_state = 1;
105 static struct timeval start;
106 static int sequential;
107 static int disk_util = 100;
108 static int state_file_fd = -1;
109 static int state_restored;
110
111 static unsigned long *read_bitmap;
112 static unsigned long find_next_bit_index;
113 static unsigned long blocks_read, blocks_read_now;
114 static u64 dev_size;
115
116 static struct drand48_data random_state;
117
118 struct our_time {
119         unsigned long hour;
120         unsigned long min;
121         unsigned long sec;
122 };
123
124 #define DRIVESCAN_MAGIC         0xabc0ffee
125 #define DRIVESCAN_VERSION       2
126
127 struct progress_state {
128         u32 magic;
129         u32 arch;
130         u32 version;
131         u32 bs;
132         u64 blocks;
133         u32 disk_util;
134         struct drand48_data random_state;
135 };
136
137 inline int test_block_read(unsigned long block)
138 {
139         unsigned long index, bitnr;
140
141         index = block / BITS_PER_LONG;
142         bitnr = block & (BITS_PER_LONG - 1);
143         return (read_bitmap[index] & (1UL << bitnr)) != 0;
144 }
145         
146 inline int mark_block_read(unsigned long block)
147 {
148         unsigned long index, bitnr, retval;
149
150         index = block / BITS_PER_LONG;
151         bitnr = block & (BITS_PER_LONG - 1);
152
153         retval = (read_bitmap[index] & (1UL << bitnr)) != 0;
154         read_bitmap[index] |= 1UL << bitnr;
155         return retval;
156 }
157
158 inline unsigned long find_next_unread_block(void)
159 {
160         unsigned int i, bitnr;
161
162         for (i = find_next_bit_index; i < BITMAP_BLOCKS(dev_size); i++) {
163                 if (read_bitmap[i] == ~0UL)
164                         continue;
165
166                 goto gotit;
167         }
168
169         return -1;
170 gotit:
171         find_next_bit_index = i;
172         bitnr = ffz(read_bitmap[i]);
173         read_bitmap[i] |= 1UL << bitnr;
174         return bitnr + BITS_PER_LONG * i;
175 }
176
177 int load_progress_state(void)
178 {
179         struct progress_state state;
180         unsigned long i;
181         int ret;
182
183         if (state_file_fd == -1)
184                 return 1;
185
186         if (lseek(state_file_fd, 0, SEEK_SET) == -1) {
187                 perror("state file seek");
188                 return 1;
189         }
190
191         ret = read(state_file_fd, &state, sizeof(state));
192         if (ret == -1) {
193                 perror("load progress file");
194                 return 1;
195         } else if (!ret)
196                 return 0;
197
198         if (state.magic != DRIVESCAN_MAGIC) {
199                 printf("Bad magic %lx\n", (unsigned long) state.magic);
200                 return 1;
201         }
202         if (state.version != DRIVESCAN_VERSION) {
203                 printf("Unsupported file version (%d)\n", (int) state.version);
204                 return 1;
205         }
206         if (state.arch != arch) {
207                 printf("State file from different arch\n");
208                 return 1;
209         }
210
211         read_bitmap = malloc(BITMAP_SIZE(state.blocks));
212         if (!read_bitmap) {
213                 printf("Unable to allocate bitmap (blocks=%Lu)\n", (unsigned long long) state.blocks);
214                 return 1;
215         }
216         memset(read_bitmap, 0, BITMAP_SIZE(dev_size));
217
218         if (read(state_file_fd, (char *) read_bitmap, BITMAP_SIZE(state.blocks)) < BITMAP_SIZE(state.blocks)) {
219                 printf("Unable to read bitmap\n");
220                 return 1;
221         }
222
223         blocks_read = 0;
224         for (i = 0; i < state.blocks; i++)
225                 if (test_block_read(i))
226                         blocks_read++;
227
228         if (blocks_read == dev_size)
229                 return 0;
230
231         memcpy(&random_state, &state.random_state, sizeof(random_state));
232
233         dev_size = state.blocks;
234         block_size = state.bs;
235         disk_util = state.disk_util;
236         printf("Restored state: %lu blocks\n", blocks_read);
237         state_restored = 1;
238         return 0;
239 }
240
241 void save_progress_state(void)
242 {
243         struct progress_state state;
244         int ret;
245
246         if (ftruncate(state_file_fd, 0) == -1) {
247                 perror("truncate state file");
248                 return;
249         }
250         if (lseek(state_file_fd, 0, SEEK_SET) == -1) {
251                 perror("lseek state file");
252                 return;
253         }
254
255         memset(&state, 0, sizeof(state));
256         state.magic = DRIVESCAN_MAGIC;
257         state.arch = arch;
258         state.version = DRIVESCAN_VERSION;
259         state.bs = block_size;
260         state.blocks = dev_size;
261         state.disk_util = disk_util;
262         memcpy(&state.random_state, &random_state, sizeof(random_state));
263
264         ret = write(state_file_fd, &state, sizeof(state));
265         if (ret == -1) {
266                 perror("write state file");
267                 return;
268         }
269
270         ret = write(state_file_fd, (char *) read_bitmap, BITMAP_SIZE(dev_size));
271         if (ret == -1) {
272                 perror("write state bitmap");
273                 return;
274         }
275
276         fsync(state_file_fd);
277         printf("Saved state: %lu blocks\n", blocks_read);
278 }
279
280 unsigned long utime_since(struct timeval *s)
281 {
282         double sec, usec;
283         struct timeval now;
284
285         gettimeofday(&now, NULL);
286         sec = now.tv_sec - s->tv_sec;
287         usec = now.tv_usec - s->tv_usec;
288         if (sec > 0 && usec < 0) {
289                 sec--;
290                 usec += 1000000;
291         }
292         return usec + (sec * (double) 1000000);
293 }
294
295 unsigned long time_since_now(void)
296 {
297         double sec, usec, ret;
298         struct timeval now;
299
300         gettimeofday(&now, NULL);
301         sec = now.tv_sec - start.tv_sec;
302         usec = now.tv_usec - start.tv_usec;
303         if (sec > 0 && usec < 0) {
304                 sec--;
305                 usec += 1000000;
306         }
307         ret = sec + usec / (double) 1000000;
308         if (ret < 0)
309                 ret = 0;
310
311         return (unsigned long) ret;
312 }
313
314 void normalize_time(struct our_time *ot, unsigned long seconds)
315 {
316         ot->hour = seconds / 3600;
317         seconds -= ot->hour * 3600;
318         ot->min = seconds / 60;
319         seconds -= ot->min * 60;
320         ot->sec = seconds;
321 }
322
323 void show_progress_state(void)
324 {
325         unsigned long rate, now;
326         struct our_time comp, left;
327
328         now = time_since_now();
329
330         normalize_time(&comp, now);
331
332         if (now == 0)
333                 now = 1;
334
335         rate = blocks_read_now / now;
336
337         memset(&left, 0, sizeof(left));
338         if (blocks_read_now)
339                 normalize_time(&left, (dev_size * now / blocks_read_now) - now);
340         
341         printf("Status:\n");
342         printf("\tRun: [%luh%lum%lusec, %luh%lum%lusec], %lu blocks/sec, %lu KiB/sec\n", comp.hour, comp.min, comp.sec, left.hour, left.min, left.sec, rate, rate * block_size / 1024);
343         printf("\tRead:[%lu, %lu]: %lu%% coverage\n", blocks_read, (unsigned long) dev_size, (unsigned long) (blocks_read * 100 / dev_size));
344 }
345
346 void sig_handler(int sig)
347 {
348         show_progress_state();
349
350         switch (sig) {
351                 case SIGINT:
352                         if (state_file[0])
353                                 save_progress_state();
354                         exit(0);
355                 case SIGALRM:
356                         if (progress_interval)
357                                 alarm(progress_interval);
358                         break;
359         }
360 }
361
362 unsigned long get_next_offset(unsigned long blocks)
363 {
364         unsigned long b, retries = dev_size;
365
366         if (!sequential) {
367                 retries = dev_size;
368                 do {
369                         long r;
370                         lrand48_r(&random_state, &r);
371                         b = (1+(double) (blocks-1) * r / (RAND_MAX+1.0));
372
373                         if (!mark_block_read(b))
374                                 break;
375                         
376                 } while (--retries);
377
378                 if (!retries) {
379                         printf("Gave up finding new block\n");
380                         sequential = 1;
381                         goto seq;
382                 }
383         } else {
384 seq:
385                 b = find_next_unread_block();
386         }
387
388         blocks_read++;
389         blocks_read_now++;
390         return b;
391 }
392
393 void idle_drive(struct timeval *s)
394 {
395         unsigned long msleep, sleep_time;
396         double ratio;
397
398         if (disk_util == 100)
399                 return;
400
401         msleep = utime_since(s);
402         ratio = (double) 100 / disk_util;
403         sleep_time = msleep * ratio - msleep;
404         usleep(sleep_time);
405 }
406
407 void read_error_report(int fd, char *buffer, unsigned long long offset)
408 {
409         unsigned long long bad_block = offset >> 9;
410         int blocks = block_size / 512;
411         int i, ret;
412
413         if (lseek(fd, offset, SEEK_SET) == -1) {
414                 perror("lseek");
415                 return;
416         }
417
418         for (i = 0; i < blocks; i++) {
419                 ret = read(fd, buffer, 512);
420
421                 if (ret == 512)
422                         continue;
423                 else if (ret < 512 && ret >= 0) {
424                         printf("Short read at sector %Lu\n", bad_block + i);
425                         continue;
426                 }
427                 if (errno == EINVAL) {
428                         printf("512-b O_DIRECT does not work\n");
429                         break;
430                 }
431                 if (errno == EIO)
432                         printf("Sector %Lu bad\n", bad_block + i);
433         }
434 }
435
436 void do_random_reads(int fd, unsigned long blocks)
437 {
438         struct timeval s;
439         char *ptr, *buffer;
440         int ret;
441
442         ptr = malloc(block_size + MASK + 1);
443         buffer = (char *) ALIGN(ptr);
444
445         do {
446                 off_t offset = get_next_offset(blocks);
447                 int coverage;
448
449                 offset = get_next_offset(blocks);
450                 if (offset == -1UL)
451                         break;
452
453                 offset = offset * block_size;
454
455                 if (lseek(fd, offset, SEEK_SET) == -1) {
456                         perror("lseek");
457                         break;
458                 }
459
460                 gettimeofday(&s, NULL);
461                 ret = read(fd, buffer, block_size);
462
463                 if (ret == -1) {
464                         if (errno == EINVAL) {
465                                 printf("EINVAL on read, check if O_DIRECT works\n");
466                                 break;
467                         } else if (errno == EIO) {
468                                 read_error_report(fd, buffer, offset);
469                         } else {
470                                 perror("read");
471                                 break;
472                         }
473                 }
474                 if (ret < block_size)
475                         read_error_report(fd, buffer, offset);
476
477                 coverage = blocks_read * 100 / dev_size;
478                 if (!sequential && coverage >= RANDOM_COVERAGE) {
479                         printf("Switching to scan for rest of blocks\n");
480                         sequential = 1;
481                 }
482
483                 if (blocks_read == dev_size)
484                         break;
485
486                 idle_drive(&s);
487
488         } while (1);
489
490         show_progress_state();
491
492         free(ptr);
493 }
494
495 int seed_randomizer(void)
496 {
497         unsigned long seed;
498         int fd;
499
500         fd = open("/dev/random", O_RDONLY);
501         if (fd == -1) {
502                 perror("open rand");
503                 return 1;
504         }
505
506         if (read(fd, &seed, sizeof(seed)) < (int) sizeof(seed)) {
507                 perror("read rand");
508                 return 1;
509         }
510
511         close(fd);
512         
513         printf("Randomizer seed: %lx\n", seed);
514         srand48_r(seed, &random_state);
515         return 0;
516 }
517
518 void device_to_state_file(void)
519 {
520         unsigned int i, offset;
521
522         offset = sprintf(state_file, "scanstate");
523
524         for (i = 0; i < strlen(device); i++) {
525                 if (device[i] == '/')
526                         state_file[offset+i] = '_';
527                 else
528                         state_file[offset+i] = device[i];
529         }
530 }
531
532 void init_state_file(void)
533 {
534         if (!state_file[0])
535                 device_to_state_file();
536
537         state_file_fd = open(state_file, O_RDWR | O_CREAT, 0644);
538
539         if (state_file_fd == -1) {
540                 perror("open state file");
541                 return;
542         }
543 }
544
545 int get_options(int argc, char *argv[])
546 {
547         static struct option longoptions[] = {
548                 { "device",     1,      NULL,   'd' },
549                 { "block size", 1,      NULL,   'b' },
550                 { "progress",   1,      NULL,   'p' },
551                 { "file",       1,      NULL,   'f' },
552                 { "restore",    1,      NULL,   'r' },
553                 { "utilization",1,      NULL,   'i' },
554         };
555         int c, res = 0;
556
557         while ((c = getopt_long(argc, argv, "d:b:p:f:ru:", longoptions, &res)) != -1) {
558                 switch (c) {
559                         case 'd':
560                                 strncpy(device, optarg, sizeof(device) - 1);
561                                 break;
562                         case 'b':
563                                 block_size = atoi(optarg);
564                                 if (block_size < 1 || block_size > 1024) {
565                                         printf("bad block size %d\n", block_size);
566                                         block_size = BS;
567                                 }
568                                 block_size <<= 10;
569                                 break;
570                         case 'p':
571                                 progress_interval = atoi(optarg);
572                                 break;
573                         case 'f':
574                                 strncpy(state_file, optarg, sizeof(state_file)-1);
575                                 break;
576                         case 'r':
577                                 restore_state = !!atoi(optarg);
578                                 break;
579                         case 'u':
580                                 disk_util = atoi(optarg);
581                                 if (disk_util < 0)
582                                         disk_util = 1;
583                                 if (disk_util > 100)
584                                         disk_util = 100;
585                                 break;
586                 }
587         }
588
589         if (!device[0]) {
590                 printf("Must give device with -d\n");
591                 return 1;
592         }
593         if (!block_size)
594                 block_size = BS;
595
596         return 0;
597 }
598
599 int main(int argc, char *argv[])
600 {
601         u64 blocks, size;
602         struct stat statbuf;
603         int fd;
604
605         if (get_options(argc, argv))
606                 return 1;
607
608         seed_randomizer();
609
610         init_state_file();
611
612         if (restore_state)
613                 load_progress_state();
614
615         if (stat(device, &statbuf) == -1) {
616                 perror("stat");
617                 return 1;
618         }
619
620         if (!S_ISBLK(statbuf.st_mode)) {
621                 printf("%s does not appear to be a block device\n", device);
622                 return 1;
623         }
624
625         fd = open(device, O_RDONLY | O_DIRECT | O_LARGEFILE);
626         if (fd == -1) {
627                 perror("open");
628                 return 2;
629         }
630
631         if (ioctl(fd, BLKGETSIZE64, &size) == -1) {
632                 perror("BLKGETSIZE64");
633                 return 3;
634         }
635
636         blocks = size / block_size;
637
638         if (state_restored && dev_size != blocks) {
639                 printf("Device size seems different %lu != %lu\n", (unsigned long) dev_size, (unsigned long) blocks);
640                 return 1;
641         }
642
643         dev_size = blocks;
644
645         if (!read_bitmap) {
646                 read_bitmap = malloc(BITMAP_SIZE(dev_size));
647                 memset(read_bitmap, 0, BITMAP_SIZE(dev_size));
648         }
649
650         signal(SIGUSR1, sig_handler);
651         signal(SIGINT, sig_handler);
652
653         if (progress_interval) {
654                 signal(SIGALRM, sig_handler);
655                 alarm(progress_interval);
656         }
657
658         gettimeofday(&start, NULL);
659         do_random_reads(fd, blocks);
660
661         save_progress_state();
662
663         free((void *) read_bitmap);
664         close(fd);
665         if (state_file_fd != -1)
666                 close(state_file_fd);
667
668         return 0;
669 }