Add possibility to make sequential IO "holed"

For sequential IO, it is now possible to add a number of bytes to
be skipped for every block read or written. Using:

bs=8k
rw=read:8k

will first read 0k->8k, then 16k->24k, and so on. This skips 8k
for every 'bs' sized block read. Similar for writes, doing

bs=4k
rw=write:4k

will write 0k->4k, then 8k->12k, etc. End result being that every
other block is written, in a sequential fashion.

Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
diff --git a/HOWTO b/HOWTO
index 72a29a9..a1b2e8c 100644
--- a/HOWTO
+++ b/HOWTO
@@ -319,8 +319,12 @@
 		a number of IO's to do before getting a new offset, this is
 		one by appending a ':<nr>' to the end of the string given.
 		For a random read, it would look like 'rw=randread:8' for
-		passing in an offset modifier with a value of 8. See the
-		'rw_sequencer' option.
+		passing in an offset modifier with a value of 8. If the
+		postfix is used with a sequential IO pattern, then the value
+		specified will be added to the generated offset for each IO.
+		For instance, using rw=write:4k will skip 4k for every
+		write. It turns sequential IO into sequential IO with holes.
+		See the 'rw_sequencer' option.
 
 rw_sequencer=str If an offset modifier is given by appending a number to
 		the rw=<str> line, then this option controls how that
diff --git a/fio.1 b/fio.1
index 40940f3..488896c 100644
--- a/fio.1
+++ b/fio.1
@@ -181,7 +181,10 @@
 specify a number of IO's to do before getting a new offset, this is one by
 appending a `:\fI<nr>\fR to the end of the string given. For a random read, it
 would look like \fBrw=randread:8\fR for passing in an offset modifier with a
-value of 8. See the \fBrw_sequencer\fR option.
+value of 8. If the postfix is used with a sequential IO pattern, then the value
+specified will be added to the generated offset for each IO. For instance,
+using \fBrw=write:4k\fR will skip 4k for every write. It turns sequential IO
+into sequential IO with holes. See the \fBrw_sequencer\fR option.
 .RE
 .TP
 .BI rw_sequencer \fR=\fPstr
diff --git a/fio.h b/fio.h
index 6c57496..9d2a61c 100644
--- a/fio.h
+++ b/fio.h
@@ -254,6 +254,7 @@
 	unsigned int rw_seq;
 	unsigned int kb_base;
 	unsigned int ddir_seq_nr;
+	unsigned long ddir_seq_add;
 	unsigned int iodepth;
 	unsigned int iodepth_low;
 	unsigned int iodepth_batch;
diff --git a/io_u.c b/io_u.c
index 51da223..6a53bda 100644
--- a/io_u.c
+++ b/io_u.c
@@ -249,7 +249,12 @@
 	assert(ddir_rw(ddir));
 
 	if (f->last_pos < f->real_file_size) {
-		*b = (f->last_pos - f->file_offset) / td->o.min_bs[ddir];
+		unsigned long long pos = f->last_pos - f->file_offset;
+
+		if (pos)
+			pos += td->o.ddir_seq_add;
+
+		*b = pos / td->o.min_bs[ddir];
 		return 0;
 	}
 
diff --git a/options.c b/options.c
index 3d8a720..6a87e98 100644
--- a/options.c
+++ b/options.c
@@ -203,11 +203,26 @@
 	char *nr = get_opt_postfix(str);
 
 	td->o.ddir_seq_nr = 1;
-	if (nr) {
+	td->o.ddir_seq_add = 0;
+
+	if (!nr)
+		return 0;
+
+	if (td_random(td))
 		td->o.ddir_seq_nr = atoi(nr);
-		free(nr);
+	else {
+		long long val;
+
+		if (str_to_decimal(nr, &val, 1, td)) {
+			log_err("fio: rw postfix parsing failed\n");
+			free(nr);
+			return 1;
+		}
+
+		td->o.ddir_seq_add = val;
 	}
 
+	free(nr);
 	return 0;
 }