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