Add rw_sequencer option
authorJens Axboe <jaxboe@fusionio.com>
Tue, 20 Jul 2010 20:46:00 +0000 (14:46 -0600)
committerJens Axboe <jaxboe@fusionio.com>
Tue, 20 Jul 2010 20:46:00 +0000 (14:46 -0600)
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
HOWTO
file.h
filesetup.c
fio.1
fio.h
init.c
io_u.c
ioengine.h
options.c

diff --git a/HOWTO b/HOWTO
index bd5bebfc929fc1a71a09e15dba27c10d43e0a042..0ef7ca42120d175c3115207861f50b964a07958e 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -313,13 +313,30 @@ rw=str            Type of io pattern. Accepted values are:
                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
                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
                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
 
 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 30293fc9ddc34e4515a56cc8f5ab0091eb254030..3b4ed0b9acadf206392f3826854d67fac7407f00 100644 (file)
--- a/file.h
+++ b/file.h
@@ -73,6 +73,7 @@ struct fio_file {
        unsigned long long io_size;
 
        unsigned long long last_pos;
        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;
 
        unsigned long long first_write;
        unsigned long long last_write;
@@ -155,6 +156,7 @@ static inline void fio_file_reset(struct fio_file *f)
 {
        f->last_free_lookup = 0;
        f->last_pos = f->file_offset;
 {
        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));
        f->file_pos = -1ULL;
        if (f->file_map)
                memset(f->file_map, 0, f->num_maps * sizeof(int));
index f0883901b466969ffa6499946bf9801f06844610..d2b74d7f6a60fd7a5adece91a8445bb9ad696bde 100644 (file)
@@ -909,6 +909,7 @@ int add_file(struct thread_data *td, const char *fname)
        }
 
        f->fd = -1;
        }
 
        f->fd = -1;
+       fio_file_reset(f);
 
        if (td->files_size <= td->files_index) {
                int new_size = td->o.nr_files + 1;
 
        if (td->files_size <= td->files_index) {
                int new_size = td->o.nr_files + 1;
@@ -1136,6 +1137,7 @@ void dup_files(struct thread_data *td, struct thread_data *org)
                        assert(0);
                }
                __f->fd = -1;
                        assert(0);
                }
                __f->fd = -1;
+               fio_file_reset(__f);
 
                if (f->file_name) {
                        __f->file_name = smalloc_strdup(f->file_name);
 
                if (f->file_name) {
                        __f->file_name = smalloc_strdup(f->file_name);
diff --git a/fio.1 b/fio.1
index 77773a0cb42db641fc1fd713b8a4130ba6b6a908..c5c10af44ae6c3df8d17980bd3e2b883d5cc717a 100644 (file)
--- a/fio.1
+++ b/fio.1
@@ -172,11 +172,39 @@ Mixed sequential reads and writes.
 Mixed random reads and writes.
 .RE
 .P
 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
 .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
 .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 e9fdc68f572667586e240fa3f4cb142c36403060..de76e6599cacbb5d29e2f0edbf33988cc460375d 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -60,6 +60,14 @@ enum fio_memtype {
        MEM_MMAPHUGE,   /* memory mapped huge file */
 };
 
        MEM_MMAPHUGE,   /* memory mapped huge file */
 };
 
+/*
+ * offset generator types
+ */
+enum {
+       RW_SEQ_SEQ      = 0,
+       RW_SEQ_IDENT,
+};
+
 /*
  * How many depth levels to log
  */
 /*
  * How many depth levels to log
  */
@@ -145,6 +153,7 @@ struct thread_options {
        char *opendir;
        char *ioengine;
        enum td_ddir td_ddir;
        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;
        unsigned int kb_base;
        unsigned int ddir_seq_nr;
        unsigned int iodepth;
diff --git a/init.c b/init.c
index bc1bb9334e1c646d3089d8e3fdf04a9a20faf82c..09b9152ae29d47a0ce252715495b92d6bc607c6f 100644 (file)
--- a/init.c
+++ b/init.c
@@ -557,7 +557,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
        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->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;
 
        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 bb00559c04a8ac5f4917ee8d3b6832b90f076007..3eeade2647a19a260d6a5a84571704699481be18 100644 (file)
--- a/io_u.c
+++ b/io_u.c
@@ -41,10 +41,12 @@ static void mark_random_map(struct thread_data *td, struct io_u *io_u)
        struct fio_file *f = io_u->file;
        unsigned long long block;
        unsigned int blocks, nr_blocks;
        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;
 
        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;
 
        while (nr_blocks) {
                unsigned int this_blocks, mask;
@@ -54,6 +56,10 @@ static void mark_random_map(struct thread_data *td, struct io_u *io_u)
                 * If we have a mixed random workload, we may
                 * encounter blocks we already did IO to.
                 */
                 * 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;
 
                if ((td->o.ddir_seq_nr == 1) && !random_map_free(f, block))
                        break;
 
@@ -190,6 +196,62 @@ static int get_next_rand_offset(struct thread_data *td, struct fio_file *f,
        return get_next_free_block(td, f, ddir, b);
 }
 
        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
 /*
  * 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 @@ static int __get_next_offset(struct thread_data *td, struct io_u *io_u)
        struct fio_file *f = io_u->file;
        unsigned long long b;
        enum fio_ddir ddir = io_u->ddir;
        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;
                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];
        }
 
        io_u->offset = b * td->o.ba[ddir];
@@ -977,6 +1029,7 @@ struct io_u *get_io_u(struct thread_data *td)
                        goto err_put;
                }
 
                        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)
                f->last_pos = io_u->offset + io_u->buflen;
 
                if (td->o.verify != VERIFY_NONE && io_u->ddir == DDIR_WRITE)
@@ -1042,7 +1095,7 @@ static void io_completed(struct thread_data *td, struct io_u *io_u,
 
        td_io_u_lock(td);
        assert(io_u->flags & IO_U_F_FLIGHT);
 
        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)) {
        td_io_u_unlock(td);
 
        if (ddir_sync(io_u->ddir)) {
index ff3069bf73f8a7dc724a85aa4cfc394572be5e00..389e95a560c0006671a2c2ee21f680d18dbe2e6e 100644 (file)
@@ -8,6 +8,7 @@ enum {
        IO_U_F_FLIGHT           = 1 << 1,
        IO_U_F_FREE_DEF         = 1 << 2,
        IO_U_F_IN_CUR_DEPTH     = 1 << 3,
        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,
 };
 
 /*
 };
 
 /*
index c9fb88625b5c2ac6754ff28bc4511c917057c74d..10e58dbad5caad8c56be621c2df57fe5e3e4ed29 100644 (file)
--- a/options.c
+++ b/options.c
@@ -881,6 +881,24 @@ static struct fio_option options[FIO_MAX_OPTS] = {
                          },
                },
        },
                          },
                },
        },
+       {
+               .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,
        {
                .name   = "ioengine",
                .type   = FIO_OPT_STR_STORE,