[PATCH] Support for specifying exact io pattern
authorJens Axboe <axboe@suse.de>
Fri, 26 May 2006 17:27:29 +0000 (19:27 +0200)
committerJens Axboe <axboe@suse.de>
Fri, 26 May 2006 17:27:29 +0000 (19:27 +0200)
iolog=foo option added, when given fio will read the io pattern
from it and replay it.

README
fio-ini.c
fio.c
fio.h

diff --git a/README b/README
index 3cf3f13395414bcbcf19c0b6246092fd7216455e..f6094a62f2d84e63289fb60ce1b90da82fc737d3 100644 (file)
--- a/README
+++ b/README
@@ -86,6 +86,11 @@ The <jobs> format is as follows:
                        can be used to gauge hard drive speed over the entire
                        platter, without reading everything. Both x/y can
                        include k/m/g suffix.
+       iolog=x         Open and read io pattern from file 'x'. The file must
+                       contain one io action per line in the following format:
+                       rw, offset, length
+                       where with rw=0/1 for read/write, and the offset
+                       and length entries being in bytes.
 
 
 Examples using a job file
index b360e9715ba169b215a6f2cd31115415c6a75fb5..126e32520448c489d31515a1803d57b7c9028526 100644 (file)
--- a/fio-ini.c
+++ b/fio-ini.c
@@ -81,11 +81,11 @@ static void setup_log(struct io_log **log)
 
 void finish_log(struct thread_data *td, struct io_log *log, const char *name)
 {
-       char file_name[128];
+       char file_name[256];
        FILE *f;
        unsigned int i;
 
-       sprintf(file_name, "client%d_%s.log", td->thread_number, name);
+       snprintf(file_name, 200, "client%d_%s.log", td->thread_number, name);
        f = fopen(file_name, "w");
        if (!f) {
                perror("fopen log");
@@ -218,7 +218,7 @@ static int add_job(struct thread_data *td, const char *jobname, int prioclass,
                else
                        sprintf(td->file_name, "%s.%d", jobname, td->jobnum);
        } else
-               strcpy(td->file_name, jobname);
+               strncpy(td->file_name, jobname, sizeof(td->file_name) - 1);
 
        sem_init(&td->mutex, 0, 0);
 
@@ -391,6 +391,14 @@ static void strip_blank_end(char *p)
        }
 }
 
+static void terminate_line(char *p)
+{
+       while (*p != '\n' && *p != '\0')
+               p++;
+
+       *p = '\0';
+}
+
 typedef int (str_cb_fn)(struct thread_data *, char *);
 
 static int check_str(char *p, char *name, str_cb_fn *cb, struct thread_data *td)
@@ -620,6 +628,13 @@ static int str_ioengine_cb(struct thread_data *td, char *str)
        return 1;
 }
 
+static int str_iolog_cb(struct thread_data *td, char *file)
+{
+       terminate_line(file);
+       strncpy(td->iolog_file, file, sizeof(td->iolog_file) - 1);
+
+       return 0;
+}
 
 int parse_jobs_ini(char *file)
 {
@@ -634,7 +649,7 @@ int parse_jobs_ini(char *file)
 
        f = fopen(file, "r");
        if (!f) {
-               perror("fopen");
+               perror("fopen job file");
                return 1;
        }
 
@@ -664,6 +679,7 @@ int parse_jobs_ini(char *file)
                                continue;
                        if (strstr(p, "["))
                                break;
+
                        if (!check_int(p, "prio", &prio)) {
 #ifndef FIO_HAVE_IOPRIO
                                fprintf(stderr, "io priorities not available\n");
@@ -829,8 +845,14 @@ int parse_jobs_ini(char *file)
                                fgetpos(f, &off);
                                continue;
                        }
+                       if (!check_str(p, "iolog", str_iolog_cb, td)) {
+                               td->iolog = 1;
+                               fgetpos(f, &off);
+                               continue;
+                       }
 
                        printf("Client%d: bad option %s\n",td->thread_number,p);
+                       return 1;
                }
                fsetpos(f, &off);
 
diff --git a/fio.c b/fio.c
index 8daa660b88b9805658445eae6422864aa85625f2..bc0083433a445ed688bdf04c11b63dab224ba524 100644 (file)
--- a/fio.c
+++ b/fio.c
@@ -609,13 +609,8 @@ static void populate_io_u(struct thread_data *td, struct io_u *io_u)
        memcpy(io_u->buf, &hdr, sizeof(hdr));
 }
 
-static int td_io_prep(struct thread_data *td, struct io_u *io_u, int read)
+static int td_io_prep(struct thread_data *td, struct io_u *io_u)
 {
-       if (read)
-               io_u->ddir = DDIR_READ;
-       else
-               io_u->ddir = DDIR_WRITE;
-
        if (td->io_prep && td->io_prep(td, io_u))
                return 1;
 
@@ -629,6 +624,41 @@ void put_io_u(struct thread_data *td, struct io_u *io_u)
        td->cur_depth--;
 }
 
+static int fill_io_u(struct thread_data *td, struct io_u *io_u)
+{
+       /*
+        * If using an iolog, grab next piece if any available.
+        */
+       if (td->iolog) {
+               struct io_piece *ipo;
+
+               if (list_empty(&td->io_log_list))
+                       return 1;
+
+               ipo = list_entry(td->io_log_list.next, struct io_piece, list);
+               list_del(&ipo->list);
+               io_u->offset = ipo->offset;
+               io_u->buflen = ipo->len;
+               io_u->ddir = ipo->ddir;
+               free(ipo);
+               return 0;
+       }
+
+       /*
+        * No log, let the seq/rand engine retrieve the next position.
+        */
+       if (!get_next_offset(td, &io_u->offset)) {
+               io_u->buflen = get_next_buflen(td);
+
+               if (io_u->buflen) {
+                       io_u->ddir = get_rw_ddir(td);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
 #define queue_full(td) (list_empty(&(td)->io_u_freelist))
 
 struct io_u *__get_io_u(struct thread_data *td)
@@ -660,13 +690,7 @@ static struct io_u *get_io_u(struct thread_data *td)
                td->last_pos += td->zone_skip;
        }
 
-       if (get_next_offset(td, &io_u->offset)) {
-               put_io_u(td, io_u);
-               return NULL;
-       }
-
-       io_u->buflen = get_next_buflen(td);
-       if (!io_u->buflen) {
+       if (fill_io_u(td, io_u)) {
                put_io_u(td, io_u);
                return NULL;
        }
@@ -679,7 +703,7 @@ static struct io_u *get_io_u(struct thread_data *td)
                return NULL;
        }
 
-       if (!td->sequential)
+       if (!td->iolog && !td->sequential)
                mark_random_map(td, io_u);
 
        td->last_pos += io_u->buflen;
@@ -687,7 +711,7 @@ static struct io_u *get_io_u(struct thread_data *td)
        if (td->verify != VERIFY_NONE)
                populate_io_u(td, io_u);
 
-       if (td_io_prep(td, io_u, get_rw_ddir(td))) {
+       if (td_io_prep(td, io_u)) {
                put_io_u(td, io_u);
                return NULL;
        }
@@ -702,8 +726,7 @@ static inline void td_set_runstate(struct thread_data *td, int runstate)
        td->runstate = runstate;
 }
 
-static int get_next_verify(struct thread_data *td,
-                          unsigned long long *offset, unsigned int *len)
+static int get_next_verify(struct thread_data *td, struct io_u *io_u)
 {
        struct io_piece *ipo;
 
@@ -713,8 +736,9 @@ static int get_next_verify(struct thread_data *td,
        ipo = list_entry(td->io_hist_list.next, struct io_piece, list);
        list_del(&ipo->list);
 
-       *offset = ipo->offset;
-       *len = ipo->len;
+       io_u->offset = ipo->offset;
+       io_u->buflen = ipo->len;
+       io_u->ddir = DDIR_READ;
        free(ipo);
        return 0;
 }
@@ -767,6 +791,68 @@ static void log_io_piece(struct thread_data *td, struct io_u *io_u)
        list_add(&ipo->list, entry);
 }
 
+static int init_iolog(struct thread_data *td)
+{
+       unsigned long long offset;
+       unsigned int bytes;
+       char *str, *p;
+       FILE *f;
+       int rw, i, reads, writes;
+
+       if (!td->iolog)
+               return 0;
+
+       f = fopen(td->iolog_file, "r");
+       if (!f) {
+               perror("fopen iolog");
+               return 1;
+       }
+
+       str = malloc(4096);
+       reads = writes = i = 0;
+       while ((p = fgets(str, 4096, f)) != NULL) {
+               struct io_piece *ipo;
+
+               if (sscanf(p, "%d,%llu,%u", &rw, &offset, &bytes) != 3) {
+                       fprintf(stderr, "bad iolog: %s\n", p);
+                       continue;
+               }
+               if (rw == DDIR_READ)
+                       reads++;
+               else if (rw == DDIR_WRITE)
+                       writes++;
+               else {
+                       fprintf(stderr, "bad ddir: %d\n", rw);
+                       continue;
+               }
+
+               ipo = malloc(sizeof(*ipo));
+               INIT_LIST_HEAD(&ipo->list);
+               ipo->offset = offset;
+               ipo->len = bytes;
+               if (bytes > td->max_bs)
+                       td->max_bs = bytes;
+               ipo->ddir = rw;
+               list_add_tail(&ipo->list, &td->io_log_list);
+               i++;
+       }
+
+       free(str);
+       fclose(f);
+
+       if (!i)
+               return 1;
+
+       if (reads && !writes)
+               td->ddir = DDIR_READ;
+       else if (!reads && writes)
+               td->ddir = DDIR_READ;
+       else
+               td->iomix = 1;
+
+       return 0;
+}
+
 static int sync_td(struct thread_data *td)
 {
        if (td->io_sync)
@@ -910,12 +996,12 @@ static void do_verify(struct thread_data *td)
                if (!io_u)
                        break;
 
-               if (get_next_verify(td, &io_u->offset, &io_u->buflen)) {
+               if (get_next_verify(td, io_u)) {
                        put_io_u(td, io_u);
                        break;
                }
 
-               if (td_io_prep(td, io_u, 1)) {
+               if (td_io_prep(td, io_u)) {
                        put_io_u(td, io_u);
                        break;
                }
@@ -1145,10 +1231,6 @@ static int init_io_u(struct thread_data *td)
                }
        }
 
-       INIT_LIST_HEAD(&td->io_u_freelist);
-       INIT_LIST_HEAD(&td->io_u_busylist);
-       INIT_LIST_HEAD(&td->io_hist_list);
-
        p = ALIGN(td->orig_buffer);
        for (i = 0; i < max_units; i++) {
                io_u = malloc(sizeof(*io_u));
@@ -1721,6 +1803,11 @@ static void *thread_main(void *data)
 
        td->pid = getpid();
 
+       INIT_LIST_HEAD(&td->io_u_freelist);
+       INIT_LIST_HEAD(&td->io_u_busylist);
+       INIT_LIST_HEAD(&td->io_hist_list);
+       INIT_LIST_HEAD(&td->io_log_list);
+
        if (init_io_u(td))
                goto err;
 
@@ -1732,6 +1819,9 @@ static void *thread_main(void *data)
        if (init_io(td))
                goto err;
 
+       if (init_iolog(td))
+               goto err;
+
        if (td->ioprio) {
                if (ioprio_set(IOPRIO_WHO_PROCESS, 0, td->ioprio) == -1) {
                        td_verror(td, errno);
@@ -1764,7 +1854,7 @@ static void *thread_main(void *data)
                do_io(td);
 
                td->runtime[td->ddir] += mtime_since_now(&td->start);
-               if (td_rw(td))
+               if (td_rw(td) && td->io_bytes[td->ddir ^ 1])
                        td->runtime[td->ddir ^ 1] = td->runtime[td->ddir];
 
                update_rusage_stat(td);
@@ -1891,7 +1981,8 @@ static void show_thread_status(struct thread_data *td,
        printf("Client%d (groupid=%d): err=%2d:\n", td->thread_number, td->groupid, td->error);
 
        show_ddir_status(td, rs, td->ddir);
-       show_ddir_status(td, rs, td->ddir ^ 1);
+       if (td->io_bytes[td->ddir ^ 1])
+               show_ddir_status(td, rs, td->ddir ^ 1);
 
        if (td->runtime[0] + td->runtime[1]) {
                double runt = td->runtime[0] + td->runtime[1];
diff --git a/fio.h b/fio.h
index 37dabae1ec0af62c7718332706b0a9041336ae94..e997fbe52f3c6ae900a5909f8c1bd44452f4b26d 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -38,6 +38,7 @@ struct io_piece {
        struct list_head list;
        unsigned long long offset;
        unsigned int len;
+       int ddir;
 };
 
 /*
@@ -148,6 +149,9 @@ struct thread_data {
        unsigned int iodepth;
        os_cpu_mask_t cpumask;
        unsigned int jobnum;
+       unsigned int iolog;
+
+       char iolog_file[256];
 
        struct drand48_data bsrange_state;
        struct drand48_data verify_state;
@@ -218,6 +222,7 @@ struct thread_data {
        unsigned int override_sync;
 
        struct list_head io_hist_list;
+       struct list_head io_log_list;
 };
 
 #define td_verror(td, err)                                             \