[PATCH] Offset verification header by a user-specified distance

Offset verification header by user specified distance.

 - Implementation is somewhat simple and probably not ideal but it works. The
   header is just swapped with the bytes at offset after the chunk has been
   filled during populate. Everything is swapped back before verify.

 - Also fixes a bug where we were relying on a moving pointer for increment
   size in populate_verify_io_u (which was working until this patch).

 - Also cleans up a couple smalls things from the header_interval patch.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/HOWTO b/HOWTO
index 2797a58..8cb849c 100644
--- a/HOWTO
+++ b/HOWTO
@@ -603,6 +603,10 @@
 		fast IO where the red-black tree sorting CPU time becomes
 		significant.
 
+header_offset=siint	Swap the verification header with data somewhere else
+			in the block before writing. Its swapped back before
+			verifying.
+
 header_interval=siint	Write the verification header at a finer granularity
 			than the blocksize. It will be written for chunks the
 			size of header_interval. blocksize should divide this
diff --git a/fio.h b/fio.h
index 653f502..e97caf1 100644
--- a/fio.h
+++ b/fio.h
@@ -396,6 +396,7 @@
 	unsigned int verify;
 	unsigned int verifysort;
 	unsigned int header_interval;
+	unsigned int header_offset;
 	unsigned int use_thread;
 	unsigned int unlink;
 	unsigned int do_disk_util;
diff --git a/options.c b/options.c
index 829b808..130dc8c 100644
--- a/options.c
+++ b/options.c
@@ -203,6 +203,17 @@
 	return add_dir_files(td, td->o.opendir);
 }
 
+static int str_header_offset_cb(void *data, unsigned int *off)
+{
+	struct thread_data *td = data;
+	if (*off && *off < sizeof(struct verify_header)) {
+		log_err("fio: header_offset too small\n");
+		return 1;
+	}
+	td->o.header_offset = *off;
+	return 0;
+}
+
 
 #define __stringify_1(x)	#x
 #define __stringify(x)		__stringify_1(x)
@@ -611,6 +622,13 @@
 		.def    = "0",
 	},
 	{
+		.name	= "header_offset",
+		.type	= FIO_OPT_STR_VAL_INT,
+		.help	= "Offset header location by N bytes",
+		.def	= "0",
+		.cb	= str_header_offset_cb,	
+	},
+	{
 		.name	= "write_iolog",
 		.type	= FIO_OPT_STR_STORE,
 		.off1	= td_var_offset(write_iolog_file),
diff --git a/verify.c b/verify.c
index a362933..8bc3457 100644
--- a/verify.c
+++ b/verify.c
@@ -32,6 +32,14 @@
 	}
 }
 
+void memswp(void* buf1, void* buf2, unsigned int len)
+{
+	struct verify_header swap;
+	memcpy(&swap, buf1, len);
+	memcpy(buf1, buf2, len);
+	memcpy(buf2, &swap, len);
+}
+
 static void hexdump(void *buffer, int len)
 {
 	unsigned char *p = buffer;
@@ -161,6 +169,9 @@
 		hdr_inc = td->o.header_interval;
 
 	for (; p < (unsigned char*) io_u->buf + io_u->buflen; p += hdr_inc) {
+		if (td->o.header_offset)
+			memswp(p, &p[td->o.header_offset], sizeof(*hdr));
+
 		hdr = (struct verify_header*) p;
 
 		if (hdr->fio_magic != FIO_HDR_MAGIC) {
@@ -229,27 +240,28 @@
  */
 void populate_verify_io_u(struct thread_data *td, struct io_u *io_u)
 {
-	const unsigned int len = io_u->buflen - sizeof(struct verify_header);
 	struct verify_header *hdr;
 	unsigned char *p = io_u->buf, *data;
-	unsigned int data_len;
+	unsigned int hdr_inc, data_len;
 
 	if (td->o.verify == VERIFY_NULL)
 		return;
 
-	fill_random_bytes(td, p, len);
+	fill_random_bytes(td, p, io_u->buflen);
 
-	for (;p < (unsigned char*) io_u->buf + io_u->buflen; p += hdr->len) {
+	hdr_inc = io_u->buflen;
+	if (td->o.header_interval)
+		hdr_inc = td->o.header_interval;
+	data_len = hdr_inc - sizeof(*hdr);
+
+	for (;p < (unsigned char*) io_u->buf + io_u->buflen; p += hdr_inc) {
 		hdr = (struct verify_header*) p;
 
 		hdr->fio_magic = FIO_HDR_MAGIC;
 		hdr->verify_type = td->o.verify;
-		hdr->len = io_u->buflen;
-		if (td->o.header_interval)
-			hdr->len = td->o.header_interval;
+		hdr->len = hdr_inc;
 
 		data = p + sizeof(*hdr);
-		data_len = hdr->len - sizeof(*hdr);
 		switch (td->o.verify) {
 		case VERIFY_MD5:
 			fill_md5(hdr, data, data_len);
@@ -270,6 +282,8 @@
 			log_err("fio: bad verify type: %d\n", td->o.verify);
 			assert(0);
 		}
+		if (td->o.header_offset)
+			memswp(p, &p[td->o.header_offset], sizeof(*hdr));
 	}
 }