Add time_based option

This allows fio to keep running, even if the workload has completed.
It will simply restart the workload over and over, for as long as the
runtime setting allows.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/HOWTO b/HOWTO
index 2cc62b2..5fdc1f7 100644
--- a/HOWTO
+++ b/HOWTO
@@ -477,6 +477,11 @@
 		a specified job will run, so this parameter is handy to
 		cap the total runtime to a given time.
 
+time_based	If set, fio will run for the duration of the runtime
+		specified even if the file(s) are completey read or
+		written. It will simply loop over the same workload
+		as many times as the runtime allows.
+
 invalidate=bool	Invalidate the buffer/page cache parts for this file prior
 		to starting io. Defaults to true.
 
diff --git a/eta.c b/eta.c
index 31028dd..39241c5 100644
--- a/eta.c
+++ b/eta.c
@@ -118,13 +118,19 @@
 		bytes_total /= (td->o.zone_skip / td->o.zone_size);
 
 	if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING) {
-		double perc;
+		double perc, perc_t;
 
 		bytes_done = td->io_bytes[DDIR_READ] + td->io_bytes[DDIR_WRITE];
 		perc = (double) bytes_done / (double) bytes_total;
 		if (perc > 1.0)
 			perc = 1.0;
 
+		if (td->o.time_based) {
+			perc_t = (double) elapsed / (double) td->o.timeout;
+			if (perc_t < perc)
+				perc = perc_t;
+		}
+
 		eta_sec = (unsigned long) (elapsed * (1.0 / perc)) - elapsed;
 
 		if (td->o.timeout && eta_sec > (td->o.timeout - elapsed))
diff --git a/fio.c b/fio.c
index d85f956..6f4fdda 100644
--- a/fio.c
+++ b/fio.c
@@ -751,6 +751,7 @@
 	unsigned long long runtime[2];
 	struct thread_data *td = data;
 	unsigned long elapsed;
+	struct timeval t;
 	int clear_state;
 
 	if (!td->o.use_thread)
@@ -824,7 +825,7 @@
 
 	runtime[0] = runtime[1] = 0;
 	clear_state = 0;
-	while (td->o.loops--) {
+	while (td->o.time_based || td->o.loops--) {
 		fio_gettime(&td->start, NULL);
 		memcpy(&td->ts.stat_sample_time, &td->start, sizeof(td->start));
 
@@ -860,6 +861,10 @@
 		if (td->error || td->terminate)
 			break;
 
+		fio_gettime(&t, NULL);
+		if (runtime_exceeded(td, &t))
+			break;
+
 		if (td->o.verify == VERIFY_NONE)
 			continue;
 
diff --git a/fio.h b/fio.h
index 9490edc..92264d9 100644
--- a/fio.h
+++ b/fio.h
@@ -413,6 +413,7 @@
 	unsigned int group_reporting;
 	unsigned int fadvise_hint;
 	unsigned int zero_buffers;
+	unsigned int time_based;
 
 	char *read_iolog_file;
 	char *write_iolog_file;
diff --git a/init.c b/init.c
index 09bbce4..b5f9abc 100644
--- a/init.c
+++ b/init.c
@@ -274,6 +274,11 @@
 		return 1;
 	}
 
+	if (!o->timeout && o->time_based) {
+		log_err("fio: time_based requires a runtime/timeout setting\n");
+		o->time_based = 0;
+	}
+
 	return 0;
 }
 
diff --git a/options.c b/options.c
index 754eb81..7a81880 100644
--- a/options.c
+++ b/options.c
@@ -478,6 +478,12 @@
 		.def	= "0",
 	},
 	{
+		.name	= "time_based",
+		.type	= FIO_OPT_STR_SET,
+		.off1	= td_var_offset(time_based),
+		.help	= "Keep running until runtime/timeout is met",
+	},
+	{
 		.name	= "mem",
 		.alias	= "iomem",
 		.type	= FIO_OPT_STR,