Support for setting rated based on IOPS
authorJens Axboe <jens.axboe@oracle.com>
Thu, 15 Mar 2007 10:41:11 +0000 (11:41 +0100)
committerJens Axboe <jens.axboe@oracle.com>
Thu, 15 Mar 2007 10:41:11 +0000 (11:41 +0100)
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
HOWTO
fio.c
fio.h
init.c
log.c
options.c
time.c

diff --git a/HOWTO b/HOWTO
index c8a84ae9d591eb1c813bee5a820a349463c9d16a..c14039318175c6170dbec9c3c7be55d80ff16801 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -418,7 +418,16 @@ thinktime_blocks
 rate=int       Cap the bandwidth used by this job to this number of KiB/sec.
 
 ratemin=int    Tell fio to do whatever it can to maintain at least this
 rate=int       Cap the bandwidth used by this job to this number of KiB/sec.
 
 ratemin=int    Tell fio to do whatever it can to maintain at least this
-               bandwidth.
+               bandwidth. Failing to meet this requirement, will cause
+               the job to exit.
+
+rate_iops=int  Cap the bandwidth to this number of IOPS. Basically the same
+               as rate, just specified independently of bandwidth. If the
+               job is given a block size range instead of a fixed value,
+               the smallest block size is used as the metric.
+
+rate_iops_min=int If fio doesn't meet this rate of IO, it will cause
+               the job to exit.
 
 ratecycle=int  Average bandwidth for 'rate' and 'ratemin' over this number
                of milliseconds.
 
 ratecycle=int  Average bandwidth for 'rate' and 'ratemin' over this number
                of milliseconds.
diff --git a/fio.c b/fio.c
index b5fada8983e90c2caeb4ca7c4c9ed1bed9a63e5a..478ef28aa7e2461365f2c30775e62949eb811400 100644 (file)
--- a/fio.c
+++ b/fio.c
@@ -103,13 +103,14 @@ static void sig_handler(int sig)
 static int check_min_rate(struct thread_data *td, struct timeval *now)
 {
        unsigned long long bytes = 0;
 static int check_min_rate(struct thread_data *td, struct timeval *now)
 {
        unsigned long long bytes = 0;
+       unsigned long iops = 0;
        unsigned long spent;
        unsigned long rate;
 
        /*
         * No minimum rate set, always ok
         */
        unsigned long spent;
        unsigned long rate;
 
        /*
         * No minimum rate set, always ok
         */
-       if (!td->ratemin)
+       if (!td->ratemin && !td->rate_iops_min)
                return 0;
 
        /*
                return 0;
 
        /*
@@ -118,32 +119,55 @@ static int check_min_rate(struct thread_data *td, struct timeval *now)
        if (mtime_since(&td->start, now) < 2000)
                return 0;
 
        if (mtime_since(&td->start, now) < 2000)
                return 0;
 
-       if (td_read(td))
+       if (td_read(td)) {
+               iops += td->io_blocks[DDIR_READ];
                bytes += td->this_io_bytes[DDIR_READ];
                bytes += td->this_io_bytes[DDIR_READ];
-       if (td_write(td))
+       }
+       if (td_write(td)) {
+               iops += td->io_blocks[DDIR_WRITE];
                bytes += td->this_io_bytes[DDIR_WRITE];
                bytes += td->this_io_bytes[DDIR_WRITE];
+       }
 
        /*
         * if rate blocks is set, sample is running
         */
 
        /*
         * if rate blocks is set, sample is running
         */
-       if (td->rate_bytes) {
+       if (td->rate_bytes || td->rate_blocks) {
                spent = mtime_since(&td->lastrate, now);
                if (spent < td->ratecycle)
                        return 0;
 
                spent = mtime_since(&td->lastrate, now);
                if (spent < td->ratecycle)
                        return 0;
 
-               if (bytes < td->rate_bytes) {
-                       log_err("%s: min rate %u not met\n", td->name, td->ratemin);
-                       return 1;
+               if (td->rate) {
+                       /*
+                        * check bandwidth specified rate
+                        */
+                       if (bytes < td->rate_bytes) {
+                               log_err("%s: min rate %u not met\n", td->name, td->ratemin);
+                               return 1;
+                       } else {
+                               rate = (bytes - td->rate_bytes) / spent;
+                               if (rate < td->ratemin || bytes < td->rate_bytes) {
+                                       log_err("%s: min rate %u not met, got %luKiB/sec\n", td->name, td->ratemin, rate);
+                                       return 1;
+                               }
+                       }
                } else {
                } else {
-                       rate = (bytes - td->rate_bytes) / spent;
-                       if (rate < td->ratemin || bytes < td->rate_bytes) {
-                               log_err("%s: min rate %u not met, got %luKiB/sec\n", td->name, td->ratemin, rate);
+                       /*
+                        * checks iops specified rate
+                        */
+                       if (iops < td->rate_iops) {
+                               log_err("%s: min iops rate %u not met\n", td->name, td->rate_iops);
                                return 1;
                                return 1;
+                       } else {
+                               rate = (iops - td->rate_blocks) / spent;
+                               if (rate < td->rate_iops_min || iops < td->rate_blocks) {
+                                       log_err("%s: min iops rate %u not met, got %lu\n", td->name, td->rate_iops_min, rate);
+                               }
                        }
                }
        }
 
        td->rate_bytes = bytes;
                        }
                }
        }
 
        td->rate_bytes = bytes;
+       td->rate_blocks = iops;
        memcpy(&td->lastrate, now, sizeof(*now));
        return 0;
 }
        memcpy(&td->lastrate, now, sizeof(*now));
        return 0;
 }
@@ -666,6 +690,7 @@ static int clear_io_state(struct thread_data *td)
        td->this_io_bytes[0] = td->this_io_bytes[1] = 0;
        td->zone_bytes = 0;
        td->rate_bytes = 0;
        td->this_io_bytes[0] = td->this_io_bytes[1] = 0;
        td->zone_bytes = 0;
        td->rate_bytes = 0;
+       td->rate_blocks = 0;
 
        td->last_was_sync = 0;
 
 
        td->last_was_sync = 0;
 
diff --git a/fio.h b/fio.h
index b7657181f6b16a656c83b8d45ba3fc6b50987594..a6adcb7bbc5b27cc00004e4a2949fcef13cb90f6 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -431,9 +431,12 @@ struct thread_data {
        unsigned int rate;
        unsigned int ratemin;
        unsigned int ratecycle;
        unsigned int rate;
        unsigned int ratemin;
        unsigned int ratecycle;
+       unsigned int rate_iops;
+       unsigned int rate_iops_min;
        unsigned long rate_usec_cycle;
        long rate_pending_usleep;
        unsigned long rate_bytes;
        unsigned long rate_usec_cycle;
        long rate_pending_usleep;
        unsigned long rate_bytes;
+       unsigned long rate_blocks;
        struct timeval lastrate;
 
        unsigned long long io_size;
        struct timeval lastrate;
 
        unsigned long long io_size;
diff --git a/init.c b/init.c
index 2fc7f0725091757bd80885356bb59c51739a0f27..15e96aacbebdea4e21ab874a6df0511aed1c7b19 100644 (file)
--- a/init.c
+++ b/init.c
@@ -135,7 +135,7 @@ static void put_job(struct thread_data *td)
  * Lazy way of fixing up options that depend on each other. We could also
  * define option callback handlers, but this is easier.
  */
  * Lazy way of fixing up options that depend on each other. We could also
  * define option callback handlers, but this is easier.
  */
-static void fixup_options(struct thread_data *td)
+static int fixup_options(struct thread_data *td)
 {
        if (!td->rwmixread && td->rwmixwrite)
                td->rwmixread = 100 - td->rwmixwrite;
 {
        if (!td->rwmixread && td->rwmixwrite)
                td->rwmixread = 100 - td->rwmixwrite;
@@ -221,6 +221,17 @@ static void fixup_options(struct thread_data *td)
 
        if (td->open_files > td->nr_files || !td->open_files)
                td->open_files = td->nr_files;
 
        if (td->open_files > td->nr_files || !td->open_files)
                td->open_files = td->nr_files;
+
+       if ((td->rate && td->rate_iops) || (td->ratemin && td->rate_iops_min)) {
+               log_err("fio: rate and rate_iops are mutually exclusive\n");
+               return 1;
+       }
+       if ((td->rate < td->ratemin) || (td->rate_iops < td->rate_iops_min)) {
+               log_err("fio: minimum rate exceeds rate\n");
+               return 1;
+       }
+
+       return 0;
 }
 
 /*
 }
 
 /*
@@ -380,7 +391,8 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
                }
        }
 
                }
        }
 
-       fixup_options(td);
+       if (fixup_options(td))
+               goto err;
 
        for_each_file(td, f, i) {
                if (td->directory && f->filetype == FIO_TYPE_FILE) {
 
        for_each_file(td, f, i) {
                if (td->directory && f->filetype == FIO_TYPE_FILE) {
diff --git a/log.c b/log.c
index 50caf3d4bed0036d452306187b97c31817ae2b48..df2edc6bb5a75ef603a923d3a6665798a97a4e2c 100644 (file)
--- a/log.c
+++ b/log.c
@@ -189,14 +189,9 @@ int setup_rate(struct thread_data *td)
        int nr_reads_per_msec;
        unsigned int bs;
 
        int nr_reads_per_msec;
        unsigned int bs;
 
-       if (!td->rate)
+       if (!td->rate && !td->rate_iops)
                return 0;
 
                return 0;
 
-       if (td->rate < td->ratemin) {
-               log_err("min rate larger than nominal rate\n");
-               return -1;
-       }
-
        if (td_rw(td))
                bs = td->rw_min_bs;
        else if (td_read(td))
        if (td_rw(td))
                bs = td->rw_min_bs;
        else if (td_read(td))
@@ -204,8 +199,12 @@ int setup_rate(struct thread_data *td)
        else
                bs = td->min_bs[DDIR_WRITE];
 
        else
                bs = td->min_bs[DDIR_WRITE];
 
-       rate = td->rate;
-       nr_reads_per_msec = (rate * 1024 * 1000) / bs;
+       if (td->rate) {
+               rate = td->rate;
+               nr_reads_per_msec = (rate * 1024 * 1000) / bs;
+       } else
+               nr_reads_per_msec = td->rate_iops * 1000;
+
        if (!nr_reads_per_msec) {
                log_err("rate lower than supported\n");
                return -1;
        if (!nr_reads_per_msec) {
                log_err("rate lower than supported\n");
                return -1;
index abd02c8c3b55055e4b73865182b0e5efa009e6f6..f502247d1eb6323b27a3960ec9e20b01ffa2c9ae 100644 (file)
--- a/options.c
+++ b/options.c
@@ -637,7 +637,19 @@ static struct fio_option options[] = {
                .name   = "ratemin",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(ratemin),
                .name   = "ratemin",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(ratemin),
-               .help   = "The bottom limit accepted",
+               .help   = "Job must meet this rate or it will be shutdown",
+       },
+       {
+               .name   = "rate_iops",
+               .type   = FIO_OPT_INT,
+               .off1   = td_var_offset(rate_iops),
+               .help   = "Limit IO used to this number of IO operations/sec",
+       },
+       {
+               .name   = "rate_iops_min",
+               .type   = FIO_OPT_INT,
+               .off1   = td_var_offset(rate_iops_min),
+               .help   = "Job must meet this rate or it will be shutdown",
        },
        {
                .name   = "ratecycle",
        },
        {
                .name   = "ratecycle",
diff --git a/time.c b/time.c
index 45a415cbb43424bf54266f3913b287e5515efd39..c5b67ff11e88eac9205359fb1c09bb3fa4db3fe7 100644 (file)
--- a/time.c
+++ b/time.c
@@ -114,7 +114,7 @@ void rate_throttle(struct thread_data *td, unsigned long time_spent,
        unsigned long usec_cycle;
        unsigned int bs;
 
        unsigned long usec_cycle;
        unsigned int bs;
 
-       if (!td->rate)
+       if (!td->rate && !td->rate_iops)
                return;
 
        if (td_rw(td))
                return;
 
        if (td_rw(td))