Add some support for a verify backlog

Instead of writing everything and then verifying everything,
allow a job to specify incremental verify stages. This can
help reduce memory consumption of fio, since we don't have
to store a huge number of verify entries to be processed
when the write phase is complete.

Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
diff --git a/fio.h b/fio.h
index 56aa9b6..2a762aa 100644
--- a/fio.h
+++ b/fio.h
@@ -185,6 +185,8 @@
 	unsigned int verify_pattern_bytes;
 	unsigned int verify_fatal;
 	unsigned int verify_async;
+	unsigned long long verify_backlog;
+	unsigned int verify_batch;
 	unsigned int use_thread;
 	unsigned int unlink;
 	unsigned int do_disk_util;
@@ -321,6 +323,7 @@
 	unsigned int ioprio;
 	unsigned int ioprio_set;
 	unsigned int last_was_sync;
+	enum fio_ddir last_ddir;
 
 	char *mmapfile;
 	int mmapfd;
@@ -335,6 +338,8 @@
 	os_random_state_t bsrange_state;
 	os_random_state_t verify_state;
 
+	unsigned int verify_batch;
+
 	int shm_id;
 
 	/*
@@ -410,6 +415,7 @@
 	 */
 	struct rb_root io_hist_tree;
 	struct flist_head io_hist_list;
+	unsigned long io_hist_len;
 
 	/*
 	 * For IO replaying
diff --git a/io_u.c b/io_u.c
index 23037f1..76ad933 100644
--- a/io_u.c
+++ b/io_u.c
@@ -926,6 +926,22 @@
 		return NULL;
 	}
 
+	if (td->o.verify_backlog && td->io_hist_len) {
+		int get_verify = 0;
+
+		if (td->verify_batch) {
+			td->verify_batch--;
+			get_verify = 1;
+		} else if (!(td->io_hist_len % td->o.verify_backlog) &&
+			 td->last_ddir != DDIR_READ) {
+			td->verify_batch = td->o.verify_batch;
+			get_verify = 1;
+		}
+
+		if (get_verify && !get_next_verify(td, io_u))
+			goto out;
+	}
+
 	/*
 	 * from a requeue, io_u already setup
 	 */
@@ -1024,6 +1040,7 @@
 	}
 
 	td->last_was_sync = 0;
+	td->last_ddir = io_u->ddir;
 
 	if (!io_u->error) {
 		unsigned int bytes = io_u->buflen - io_u->resid;
diff --git a/log.c b/log.c
index 99f20b5..6a99c66 100644
--- a/log.c
+++ b/log.c
@@ -160,12 +160,14 @@
 	while ((n = rb_first(&td->io_hist_tree)) != NULL) {
 		ipo = rb_entry(n, struct io_piece, rb_node);
 		rb_erase(n, &td->io_hist_tree);
+		td->io_hist_len--;
 		free(ipo);
 	}
 
 	while (!flist_empty(&td->io_hist_list)) {
 		ipo = flist_entry(td->io_hist_list.next, struct io_piece, list);
 		flist_del(&ipo->list);
+		td->io_hist_len--;
 		free(ipo);
 	}
 }
@@ -201,6 +203,7 @@
 	      (file_randommap(td, ipo->file) || td->o.verify == VERIFY_NONE)) {
 		INIT_FLIST_HEAD(&ipo->list);
 		flist_add_tail(&ipo->list, &td->io_hist_list);
+		td->io_hist_len++;
 		return;
 	}
 
@@ -222,6 +225,7 @@
 			p = &(*p)->rb_right;
 		else {
 			assert(ipo->len == __ipo->len);
+			td->io_hist_len--;
 			rb_erase(parent, &td->io_hist_tree);
 			goto restart;
 		}
@@ -229,6 +233,7 @@
 
 	rb_link_node(&ipo->rb_node, parent, p);
 	rb_insert_color(&ipo->rb_node, &td->io_hist_tree);
+	td->io_hist_len++;
 }
 
 void write_iolog_close(struct thread_data *td)
diff --git a/options.c b/options.c
index 2369191..f470b45 100644
--- a/options.c
+++ b/options.c
@@ -1410,6 +1410,20 @@
 		.help	= "Number of async verifier threads to use",
 		.parent	= "verify",
 	},
+	{
+		.name	= "verify_backlog",
+		.type	= FIO_OPT_STR_VAL,
+		.off1	= td_var_offset(verify_backlog),
+		.help	= "Verify after this number of blocks are written",
+		.parent	= "verify",
+	},
+	{
+		.name	= "verify_backlog_batch",
+		.type	= FIO_OPT_INT,
+		.off1	= td_var_offset(verify_batch),
+		.help	= "Verify this number of IO blocks",
+		.parent	= "verify_backlog",
+	},
 #ifdef FIO_HAVE_CPU_AFFINITY
 	{
 		.name	= "verify_async_cpus",
diff --git a/verify.c b/verify.c
index c894b60..6932cfc 100644
--- a/verify.c
+++ b/verify.c
@@ -748,8 +748,10 @@
 
 		ipo = rb_entry(n, struct io_piece, rb_node);
 		rb_erase(n, &td->io_hist_tree);
+		td->io_hist_len--;
 	} else if (!flist_empty(&td->io_hist_list)) {
 		ipo = flist_entry(td->io_hist_list.next, struct io_piece, list);
+		td->io_hist_len--;
 		flist_del(&ipo->list);
 	}