[PATCH] fio: ->verror need not be so large
[disktools.git] / drivescan.c
CommitLineData
854a84cf
JA
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
6b2a251b 21#include "arch.h"
854a84cf
JA
22
23#if BITS_PER_LONG == 32
24typedef unsigned long long u64;
25typedef unsigned long u32;
26#elif BITS_PER_LONG == 64
27typedef unsigned long u64;
28typedef unsigned int u32;
29#endif
30
31#ifndef BLKGETSIZE64
32#define BLKGETSIZE64 _IOR(0x12,114,size_t)
33#endif
34
f61c31f7 35#define BS (65536)
854a84cf
JA
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
45static char device[256];
46static char state_file[256];
47static int progress_interval;
48static int block_size;
49static int restore_state = 1;
50static struct timeval start;
51static int sequential;
52static int disk_util = 100;
c040c171 53static int disk_util_set;
854a84cf
JA
54static int state_file_fd = -1;
55static int state_restored;
56
57static unsigned long *read_bitmap;
58static unsigned long find_next_bit_index;
59static unsigned long blocks_read, blocks_read_now;
60static u64 dev_size;
61
62static struct drand48_data random_state;
63
64struct 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
73struct 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
83inline 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
92inline 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
104inline 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;
116gotit:
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
123int 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 }
5fe27a7b 152 if (state.arch != ARCH) {
854a84cf
JA
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;
c040c171
JA
181 if (!disk_util_set)
182 disk_util = state.disk_util;
854a84cf
JA
183 printf("Restored state: %lu blocks\n", blocks_read);
184 state_restored = 1;
185 return 0;
186}
187
188void 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;
5fe27a7b 204 state.arch = ARCH;
854a84cf
JA
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
227unsigned 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
242unsigned 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
261void 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
270void 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
293void 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
309unsigned 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 {
331seq:
332 b = find_next_unread_block();
333 }
334
335 blocks_read++;
336 blocks_read_now++;
337 return b;
338}
339
340void 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
354void 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
383void 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
442int 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
465void 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
479void 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
492int get_options(int argc, char *argv[])
493{
494 static struct option longoptions[] = {
495 { "device", 1, NULL, 'd' },
254abedc 496 { "block-size", 1, NULL, 'b' },
854a84cf
JA
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;
c040c171 532 disk_util_set = 1;
854a84cf
JA
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
547int 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}