Whamcloud - gitweb
badblocks: implement read throttling
authorIustin Pop <iustin@google.com>
Thu, 12 Jun 2008 07:30:04 +0000 (09:30 +0200)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 17 Jun 2008 11:47:25 +0000 (07:47 -0400)
Currently, badblocks will read as fast as it can from the drive. While
this is what one wants usually, if badblocks is run in read-only mode on
a drive that is in use, it will greatly degrade the other users of this
disk.

This patch adds a throttling mode for reads where each read will be
delayed by a percentage of the time the previous read took; i.e., an
invocation of '-d 100' will cause the sleep to be the same as the read
took, a value of 200 will cause the sleep to be twice as high, and a
value of 50 will cause it to be half.  This will not be done if the
previous read had errors, since then the hardware will possibly have
timeouts and that would decrease the speed too much.

This algorithm helps when the disk is used by other processes as then,
due to the increased load, the time spent doing the reads will be
higher, and correspondingly badblocks will sleep even more and thus it
will use less of the drive's bandwidth. This is different from using
ionice, as it is a voluntary (and partial) throttling.

Signed-off-by: Iustin Pop <iustin@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
misc/badblocks.8.in
misc/badblocks.c

index b83b598..66a14f2 100644 (file)
@@ -20,6 +20,10 @@ badblocks \- search a device for bad blocks
 .I max_bad_blocks
 ]
 [
+.B \-d
+.I read_delay_factor
+]
+[
 .B \-i
 .I input_file
 ]
@@ -90,6 +94,14 @@ Specify a maximum number of bad blocks before aborting the test.  The
 default is 0, meaning the test will continue until the end of the test
 range is reached.
 .TP
+.BI \-d " read delay factor"
+This parameter, if passed and non-zero, will cause bad blocks to sleep
+between reads if there were no errors encountered in the read
+operation; the delay will be calculated as a percentage of the time it
+took for the read operation to be performed. In other words, a value of 
+100 will cause each read to be delayed by the amount the previous read
+took, and a value of 200 by twice the amount.
+.TP
 .B \-f
 Normally, badblocks will refuse to do a read/write or a non-destructive
 test on a device which is mounted, since either can cause the system to
index 5676a84..6bd95bd 100644 (file)
@@ -71,6 +71,7 @@ static unsigned int *t_patts = NULL;  /* test patterns */
 static int current_O_DIRECT = 0;       /* Current status of O_DIRECT flag */
 static int exclusive_ok = 0;
 static unsigned int max_bb = 0;                /* Abort test if more than this number of bad blocks has been encountered */
+static unsigned int d_flag = 0;                /* delay factor between reads */
 
 #define T_INC 32
 
@@ -78,7 +79,7 @@ unsigned int sys_page_size = 4096;
 
 static void usage(void)
 {
-       fprintf(stderr, _("Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n [-c blocks_at_once] [-p num_passes] [-e max_bad_blocks] [-t test_pattern [-t test_pattern [...]]]\n device [last_block [start_block]]\n"),
+       fprintf(stderr, _("Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n [-c blocks_at_once] [-p num_passes] [-e max_bad_blocks] [-d delay_factor_between_reads] [-t test_pattern [-t test_pattern [...]]]\n device [last_block [start_block]]\n"),
                 program_name);
        exit (1);
 }
@@ -268,6 +269,9 @@ static int do_read (int dev, unsigned char * buffer, int try, int block_size,
                    blk_t current_block)
 {
        long got;
+       struct timeval tv1, tv2;
+#define NANOSEC (1000000000L)
+#define MILISEC (1000L)
 
        set_o_direct(dev, buffer, try * block_size, current_block);
 
@@ -280,12 +284,34 @@ static int do_read (int dev, unsigned char * buffer, int try, int block_size,
                com_err (program_name, errno, _("during seek"));
 
        /* Try the read */
+       if (d_flag)
+               gettimeofday(&tv1);
        got = read (dev, buffer, try * block_size);
+       if (d_flag)
+               gettimeofday(&tv2);
        if (got < 0)
                got = 0;        
        if (got & 511)
                fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
        got /= block_size;
+       if (d_flag && got == try) {
+               struct timespec ts;
+               ts.tv_sec = tv2.tv_sec - tv1.tv_sec;
+               ts.tv_nsec = (tv2.tv_usec - tv1.tv_usec) * MILISEC;
+               if (ts.tv_nsec < 0) {
+                       ts.tv_nsec += NANOSEC;
+                       ts.tv_sec -= 1;
+               }
+               /* increase/decrease the sleep time based on d_flag value */
+               ts.tv_sec = ts.tv_sec * d_flag / 100;
+               ts.tv_nsec = ts.tv_nsec * d_flag / 100;
+               if (ts.tv_nsec > NANOSEC) {
+                       ts.tv_sec += ts.tv_nsec / NANOSEC;
+                       ts.tv_nsec %= NANOSEC;
+               }
+               if (ts.tv_sec || ts.tv_nsec)
+                       nanosleep(&ts, NULL);
+       }
        return got;
 }
 
@@ -910,7 +936,7 @@ int main (int argc, char ** argv)
        
        if (argc && *argv)
                program_name = *argv;
-       while ((c = getopt (argc, argv, "b:e:fi:o:svwnc:p:h:t:X")) != EOF) {
+       while ((c = getopt (argc, argv, "b:d:e:fi:o:svwnc:p:h:t:X")) != EOF) {
                switch (c) {
                case 'b':
                        block_size = parse_uint(optarg, "block size");
@@ -953,6 +979,9 @@ int main (int argc, char ** argv)
                case 'e':
                        max_bb = parse_uint(optarg, "max bad block count");
                        break;
+               case 'd':
+                       d_flag = parse_uint(optarg, "read delay factor");
+                       break;
                case 'p':
                        num_passes = parse_uint(optarg, 
                                                "number of clean passes");