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