Add rw_sequencer option
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
diff --git a/HOWTO b/HOWTO
index bd5bebf..0ef7ca4 100644
--- a/HOWTO
+++ b/HOWTO
@@ -313,13 +313,30 @@
For the mixed io types, the default is to split them 50/50.
For certain types of io the result may still be skewed a bit,
since the speed may be different. It is possible to specify
- a number of IO's to do before getting a new offset - this
- is only useful for random IO, where fio would normally
- generate a new random offset for every IO. If you append
- eg 8 to randread, you would get a new random offset for
+ 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.
+
+rw_sequencer=str If an offset modifier is given by appending a number to
+ the rw=<str> line, then this option controls how that
+ number modifies the IO offset being generated. Accepted
+ values are:
+
+ sequential Generate sequential offset
+ identical Generate the same offset
+
+ 'sequential' is only useful for random IO, where fio would
+ normally generate a new random offset for every IO. If you
+ append eg 8 to randread, you would get a new random offset for
every 8 IO's. The result would be a seek for only every 8
IO's, instead of for every IO. Use rw=randread:8 to specify
- that.
+ that. As sequential IO is already sequential, setting
+ 'sequential' for that would not result in any differences.
+ 'identical' behaves in a similar fashion, except it sends
+ the same offset 8 number of times before generating a new
+ offset.
kb_base=int The base unit for a kilobyte. The defacto base is 2^10, 1024.
Storage manufacturers like to use 10^3 or 1000 as a base
diff --git a/file.h b/file.h
index 30293fc..3b4ed0b 100644
--- a/file.h
+++ b/file.h
@@ -73,6 +73,7 @@
unsigned long long io_size;
unsigned long long last_pos;
+ unsigned long long last_start;
unsigned long long first_write;
unsigned long long last_write;
@@ -155,6 +156,7 @@
{
f->last_free_lookup = 0;
f->last_pos = f->file_offset;
+ f->last_start = -1ULL;
f->file_pos = -1ULL;
if (f->file_map)
memset(f->file_map, 0, f->num_maps * sizeof(int));
diff --git a/filesetup.c b/filesetup.c
index f088390..d2b74d7 100644
--- a/filesetup.c
+++ b/filesetup.c
@@ -909,6 +909,7 @@
}
f->fd = -1;
+ fio_file_reset(f);
if (td->files_size <= td->files_index) {
int new_size = td->o.nr_files + 1;
@@ -1136,6 +1137,7 @@
assert(0);
}
__f->fd = -1;
+ fio_file_reset(__f);
if (f->file_name) {
__f->file_name = smalloc_strdup(f->file_name);
diff --git a/fio.1 b/fio.1
index 77773a0..c5c10af 100644
--- a/fio.1
+++ b/fio.1
@@ -172,11 +172,39 @@
Mixed random reads and writes.
.RE
.P
-For mixed I/O, the default split is 50/50. For random I/O, the number of I/Os
-to perform before getting a new offset can be specified by appending
-`:\fIint\fR' to the pattern type. The default is 1.
+For mixed I/O, the default split is 50/50. For certain types of io the result
+may still be skewed a bit, since the speed may be different. It is possible to
+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.
.RE
.TP
+.BI rw_sequencer \fR=\fPstr
+If an offset modifier is given by appending a number to the \fBrw=<str>\fR line,
+then this option controls how that number modifies the IO offset being
+generated. Accepted values are:
+.RS
+.RS
+.TP
+.B sequential
+Generate sequential offset
+.TP
+.B identical
+Generate the same offset
+.RE
+.P
+\fBsequential\fR is only useful for random IO, where fio would normally
+generate a new random offset for every IO. If you append eg 8 to randread, you
+would get a new random offset for every 8 IO's. The result would be a seek for
+only every 8 IO's, instead of for every IO. Use \fBrw=randread:8\fR to specify
+that. As sequential IO is already sequential, setting \fBsequential\fR for that
+would not result in any differences. \fBidentical\fR behaves in a similar
+fashion, except it sends the same offset 8 number of times before generating a
+new offset.
+.RE
+.P
+.TP
.BI kb_base \fR=\fPint
The base unit for a kilobyte. The defacto base is 2^10, 1024. Storage
manufacturers like to use 10^3 or 1000 as a base ten unit instead, for obvious
diff --git a/fio.h b/fio.h
index e9fdc68..de76e65 100644
--- a/fio.h
+++ b/fio.h
@@ -61,6 +61,14 @@
};
/*
+ * offset generator types
+ */
+enum {
+ RW_SEQ_SEQ = 0,
+ RW_SEQ_IDENT,
+};
+
+/*
* How many depth levels to log
*/
#define FIO_IO_U_MAP_NR 8
@@ -145,6 +153,7 @@
char *opendir;
char *ioengine;
enum td_ddir td_ddir;
+ unsigned int rw_seq;
unsigned int kb_base;
unsigned int ddir_seq_nr;
unsigned int iodepth;
diff --git a/init.c b/init.c
index bc1bb93..09b9152 100644
--- a/init.c
+++ b/init.c
@@ -557,7 +557,7 @@
td->ts.slat_stat[0].min_val = td->ts.slat_stat[1].min_val = ULONG_MAX;
td->ts.lat_stat[0].min_val = td->ts.lat_stat[1].min_val = ULONG_MAX;
td->ts.bw_stat[0].min_val = td->ts.bw_stat[1].min_val = ULONG_MAX;
- td->ddir_seq_nr = td->o.ddir_seq_nr;
+ td->ddir_seq_nr = td->o.ddir_seq_nr + 1;
if ((td->o.stonewall || td->o.new_group) && prev_group_jobs) {
prev_group_jobs = 0;
diff --git a/io_u.c b/io_u.c
index bb00559..3eeade2 100644
--- a/io_u.c
+++ b/io_u.c
@@ -41,10 +41,12 @@
struct fio_file *f = io_u->file;
unsigned long long block;
unsigned int blocks, nr_blocks;
+ int busy_check;
block = (io_u->offset - f->file_offset) / (unsigned long long) min_bs;
nr_blocks = (io_u->buflen + min_bs - 1) / min_bs;
blocks = 0;
+ busy_check = !(io_u->flags & IO_U_F_BUSY_OK);
while (nr_blocks) {
unsigned int this_blocks, mask;
@@ -54,6 +56,10 @@
* If we have a mixed random workload, we may
* encounter blocks we already did IO to.
*/
+ if (!busy_check) {
+ blocks = nr_blocks;
+ break;
+ }
if ((td->o.ddir_seq_nr == 1) && !random_map_free(f, block))
break;
@@ -190,6 +196,62 @@
return get_next_free_block(td, f, ddir, b);
}
+static int get_next_rand_block(struct thread_data *td, struct fio_file *f,
+ enum fio_ddir ddir, unsigned long long *b)
+{
+ if (get_next_rand_offset(td, f, ddir, b)) {
+ dprint(FD_IO, "%s: rand offset failed, last=%llu, size=%llu\n",
+ f->file_name, f->last_pos, f->real_file_size);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_next_seq_block(struct thread_data *td, struct fio_file *f,
+ enum fio_ddir ddir, unsigned long long *b)
+{
+ if (f->last_pos < f->real_file_size) {
+ *b = (f->last_pos - f->file_offset) / td->o.min_bs[ddir];
+ return 0;
+ }
+
+ return 1;
+}
+
+static int get_next_block(struct thread_data *td, struct io_u *io_u,
+ enum fio_ddir ddir, int rw_seq, unsigned long long *b)
+{
+ struct fio_file *f = io_u->file;
+ int ret;
+
+ if (rw_seq) {
+ if (td_random(td))
+ ret = get_next_rand_block(td, f, ddir, b);
+ else
+ ret = get_next_seq_block(td, f, ddir, b);
+ } else {
+ io_u->flags |= IO_U_F_BUSY_OK;
+
+ if (td->o.rw_seq == RW_SEQ_SEQ) {
+ ret = get_next_seq_block(td, f, ddir, b);
+ if (ret)
+ ret = get_next_rand_block(td, f, ddir, b);
+ } else if (td->o.rw_seq == RW_SEQ_IDENT) {
+ if (f->last_start != -1ULL)
+ *b = (f->last_start - f->file_offset) / td->o.min_bs[ddir];
+ else
+ *b = 0;
+ ret = 0;
+ } else {
+ log_err("fio: unknown rw_seq=%d\n", td->o.rw_seq);
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
/*
* For random io, generate a random new block and see if it's used. Repeat
* until we find a free one. For sequential io, just return the end of
@@ -200,26 +262,16 @@
struct fio_file *f = io_u->file;
unsigned long long b;
enum fio_ddir ddir = io_u->ddir;
+ int rw_seq_hit = 0;
- if (td_random(td) && (td->o.ddir_seq_nr && !--td->ddir_seq_nr)) {
+ if (td->o.ddir_seq_nr && !--td->ddir_seq_nr) {
+ rw_seq_hit = 1;
td->ddir_seq_nr = td->o.ddir_seq_nr;
+ }
- if (get_next_rand_offset(td, f, ddir, &b)) {
- dprint(FD_IO, "%s: getting rand offset failed\n",
- f->file_name);
- return 1;
- }
- } else {
- if (f->last_pos >= f->real_file_size) {
- if (!td_random(td) ||
- get_next_rand_offset(td, f, ddir, &b)) {
- dprint(FD_IO, "%s: pos %llu > size %llu\n",
- f->file_name, f->last_pos,
- f->real_file_size);
- return 1;
- }
- } else
- b = (f->last_pos - f->file_offset) / td->o.min_bs[ddir];
+ if (get_next_block(td, io_u, ddir, rw_seq_hit, &b)) {
+ printf("fail\n");
+ return 1;
}
io_u->offset = b * td->o.ba[ddir];
@@ -977,6 +1029,7 @@
goto err_put;
}
+ f->last_start = io_u->offset;
f->last_pos = io_u->offset + io_u->buflen;
if (td->o.verify != VERIFY_NONE && io_u->ddir == DDIR_WRITE)
@@ -1042,7 +1095,7 @@
td_io_u_lock(td);
assert(io_u->flags & IO_U_F_FLIGHT);
- io_u->flags &= ~IO_U_F_FLIGHT;
+ io_u->flags &= ~(IO_U_F_FLIGHT | IO_U_F_BUSY_OK);
td_io_u_unlock(td);
if (ddir_sync(io_u->ddir)) {
diff --git a/ioengine.h b/ioengine.h
index ff3069b..389e95a 100644
--- a/ioengine.h
+++ b/ioengine.h
@@ -8,6 +8,7 @@
IO_U_F_FLIGHT = 1 << 1,
IO_U_F_FREE_DEF = 1 << 2,
IO_U_F_IN_CUR_DEPTH = 1 << 3,
+ IO_U_F_BUSY_OK = 1 << 4,
};
/*
diff --git a/options.c b/options.c
index c9fb886..10e58db 100644
--- a/options.c
+++ b/options.c
@@ -882,6 +882,24 @@
},
},
{
+ .name = "rw_sequencer",
+ .type = FIO_OPT_STR,
+ .off1 = td_var_offset(rw_seq),
+ .help = "IO offset generator modifier",
+ .def = "sequential",
+ .posval = {
+ { .ival = "sequential",
+ .oval = RW_SEQ_SEQ,
+ .help = "Generate sequential offsets",
+ },
+ { .ival = "identical",
+ .oval = RW_SEQ_IDENT,
+ .help = "Generate identical offsets",
+ },
+ },
+ },
+
+ {
.name = "ioengine",
.type = FIO_OPT_STR_STORE,
.off1 = td_var_offset(ioengine),