Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
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. 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.
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 && !td->rate_iops_min)
if (mtime_since(&td->start, now) < 2000)
return 0;
if (mtime_since(&td->start, now) < 2000)
return 0;
+ 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)) {
+ 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 || 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;
+ }
+ }
- 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);
+ } 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;
}
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;
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;
* 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;
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;
+ 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) {
int nr_reads_per_msec;
unsigned int bs;
int nr_reads_per_msec;
unsigned int bs;
+ if (!td->rate && !td->rate_iops)
- 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))
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;
.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",
unsigned long usec_cycle;
unsigned int bs;
unsigned long usec_cycle;
unsigned int bs;
+ if (!td->rate && !td->rate_iops)