[PATCH] fio: make random io writes verifiable as well
authorJens Axboe <axboe@suse.de>
Fri, 4 Nov 2005 12:39:32 +0000 (13:39 +0100)
committerJens Axboe <axboe@suse.de>
Fri, 4 Nov 2005 12:39:32 +0000 (13:39 +0100)
We maintain a log of successfully written blocks, so we can just
unwind it for verification.

fio.c
list.h

diff --git a/fio.c b/fio.c
index bfc3680eb5e3bb1748eefdeb3794ed9ee46d7f87..d5c65ddff9d932cb59f4a17d89cc9399fdf6194b 100644 (file)
--- a/fio.c
+++ b/fio.c
@@ -208,6 +208,12 @@ struct io_log {
        struct io_sample *log;
 };
 
+struct io_piece {
+       struct list_head list;
+       unsigned long long offset;
+       unsigned int len;
+};
+
 #define FIO_HDR_MAGIC  0xf00baaef
 
 struct verify_header {
@@ -301,6 +307,8 @@ struct thread_data {
        struct io_log *bw_log;
 
        struct timeval start;
+
+       struct list_head io_hist_list;
 };
 
 static struct thread_data *threads;
@@ -404,8 +412,11 @@ static unsigned long long get_next_offset(struct thread_data *td)
        long r;
 
        if (!td->sequential) {
+               int min_bs_kb = td->min_bs >> 10;
+
                lrand48_r(&td->random_state, &r);
                kb = (1+(double) (td->kb-1) * r / (RAND_MAX+1.0));
+               kb = (kb + min_bs_kb - 1) & ~(min_bs_kb - 1);
        } else
                kb = td->last_kb;
 
@@ -473,7 +484,7 @@ static void add_slat_sample(struct thread_data *td, unsigned long msec)
        add_stat_sample(td, &td->slat_stat, msec);
 }
 
-static void add_bw_sample(struct thread_data *td, unsigned long msec)
+static void add_bw_sample(struct thread_data *td)
 {
        unsigned long spent = mtime_since_now(&td->stat_sample_time);
        unsigned long rate;
@@ -575,8 +586,8 @@ static inline int runtime_exceeded(struct thread_data *td, struct timeval *t)
 static void fill_random_bytes(struct thread_data *td,
                              unsigned char *p, unsigned int len)
 {
+       unsigned int todo;
        double r;
-       int todo;
 
        while (len) {
                drand48_r(&td->verify_state, &r);
@@ -596,7 +607,7 @@ static void fill_random_bytes(struct thread_data *td,
        }
 }
 
-static int verify_io_u(struct thread_data *td, struct io_u *io_u)
+static int verify_io_u(struct io_u *io_u)
 {
        struct verify_header *hdr = (struct verify_header *) io_u->buf;
        unsigned char *p = (unsigned char *) io_u->buf;
@@ -612,45 +623,6 @@ static int verify_io_u(struct thread_data *td, struct io_u *io_u)
        return memcmp(hdr->md5_digest, md5_ctx.hash, sizeof(md5_ctx.hash));
 }
 
-static int verify_io_us(struct thread_data *td, struct io_u *io_u, int *back)
-{
-       struct verify_header *hdr;
-       unsigned int left;
-       struct io_u i;
-       char *buf;
-       off_t off;
-
-       if (back)
-               *back = 0;
-
-       left = io_u->buflen;
-       buf = io_u->buf;
-       off = io_u->offset;
-       while (left) {
-               hdr = (struct verify_header *) buf;
-               i.buf = buf;
-               i.buflen = hdr->len;
-       
-               if (hdr->len > left) {
-                       if (back)
-                               *back = left;
-                       return 0;
-               }
-
-               if (verify_io_u(td, &i)) {
-                       printf("failed verify at offset %lu\n", (unsigned long) off);
-                       td->error = EBADMSG;
-                       return 1;
-               }
-
-               buf += hdr->len;
-               left -= hdr->len;
-               off += hdr->len;
-       }
-
-       return 0;
-}
-
 /*
  * fill body of io_u->buf with random data and add a header with the
  * (eg) sha1sum of that data.
@@ -730,22 +702,33 @@ 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)
+{
+       struct io_piece *ipo;
+
+       if (list_empty(&td->io_hist_list))
+               return 1;
+
+       ipo = list_entry(td->io_hist_list.next, struct io_piece, list);
+       list_del(&ipo->list);
+
+       *offset = ipo->offset;
+       *len = ipo->len;
+       free(ipo);
+       return 0;
+}
+
 static void do_sync_verify(struct thread_data *td)
 {
        struct timeval t;
        struct io_u *io_u = NULL;
-       loff_t off = 0;
-       int back, ret;
+       int ret;
 
        td_set_runstate(td, TD_VERIFYING);
 
        io_u = __get_io_u(td);
 
-       if (lseek(td->fd, 0, SEEK_SET) < 0) {
-               td->error = errno;
-               goto out;
-       }
-
        if (!td->odirect) {
                unsigned long size = td->kb << 10;
 
@@ -763,8 +746,15 @@ static void do_sync_verify(struct thread_data *td)
                if (runtime_exceeded(td, &t))
                        break;
 
-               io_u->offset = off;
-               io_u->buflen = td->max_bs;
+               if (get_next_verify(td, &io_u->offset, &io_u->buflen))
+                       break;
+
+               if (td->cur_off != io_u->offset) {
+                       if (lseek(td->fd, io_u->offset, SEEK_SET) == -1) {
+                               td->error = errno;
+                               break;
+                       }
+               }
 
                ret = read(td->fd, io_u->buf, io_u->buflen);
                if (ret < (int) io_u->buflen) {
@@ -777,19 +767,10 @@ static void do_sync_verify(struct thread_data *td)
                                io_u->buflen = ret;
                }
 
-               if (verify_io_us(td, io_u, &back))
+               if (verify_io_u(io_u))
                        break;
 
-               if (back) {
-                       printf("will seek %d %d\n", ret, back);
-                       ret -= back;
-                       if (lseek(td->fd, -back, SEEK_CUR) < 0) {
-                               td->error = errno;
-                               break;
-                       }
-               }
-
-               off += ret;
+               td->cur_off = io_u->offset + io_u->buflen;
        } while (1);
 
 out:
@@ -797,6 +778,43 @@ out:
        put_io_u(td, io_u);
 }
 
+/*
+ * log a succesful write, so we can unwind the log for verify
+ */
+static void log_io_piece(struct thread_data *td, struct io_u *io_u)
+{
+       struct io_piece *ipo = malloc(sizeof(*ipo));
+       struct list_head *entry;
+
+       INIT_LIST_HEAD(&ipo->list);
+       ipo->offset = io_u->offset;
+       ipo->len = io_u->buflen;
+
+       if (td->sequential) {
+               list_add_tail(&ipo->list, &td->io_hist_list);
+               return;
+       }
+
+       /*
+        * for random io, sort the list so verify will run faster
+        */
+       entry = &td->io_hist_list;
+       while ((entry = entry->prev) != &td->io_hist_list) {
+               struct io_piece *__ipo = list_entry(entry, struct io_piece, list);
+
+               if (__ipo->offset == ipo->offset &&
+                   __ipo->len == ipo->len) {
+                       free(ipo);
+                       ipo = NULL;
+                       break;
+               } else if (__ipo->offset < ipo->offset)
+                       break;
+       }
+
+       if (ipo)
+               list_add(&ipo->list, entry);
+}
+
 static void do_sync_io(struct thread_data *td)
 {
        unsigned long msec, usec;
@@ -831,6 +849,9 @@ static void do_sync_io(struct thread_data *td)
                        break;
                }
 
+               if (!td_read(td))
+                       log_io_piece(td, io_u);
+
                td->io_blocks++;
                td->io_kb += io_u->buflen >> 10;
                td->this_io_kb += io_u->buflen >> 10;
@@ -849,7 +870,7 @@ static void do_sync_io(struct thread_data *td)
 
                msec = usec / 1000;
                add_clat_sample(td, msec);
-               add_bw_sample(td, msec);
+               add_bw_sample(td);
 
                if (runtime_exceeded(td, &e))
                        break;
@@ -914,7 +935,10 @@ static int ios_completed(struct thread_data *td, int nr)
                msec = mtime_since(&io_u->issue_time, &e);
 
                add_clat_sample(td, msec);
-               add_bw_sample(td, msec);
+               add_bw_sample(td);
+
+               if (!td_read(td))
+                       log_io_piece(td, io_u);
 
                bytes_done += io_u->buflen;
                put_io_u(td, io_u);
@@ -961,7 +985,7 @@ static int async_do_verify(struct thread_data *td, struct io_u **io_u)
        int ret = 0;
 
        if (v_io_u) {
-               ret = verify_io_us(td, v_io_u, NULL);
+               ret = verify_io_u(v_io_u);
                put_io_u(td, v_io_u);
                *io_u = NULL;
        }
@@ -973,9 +997,7 @@ static void do_async_verify(struct thread_data *td)
 {
        struct timeval t;
        struct io_u *io_u, *v_io_u = NULL;
-       struct verify_header *hdr;
-       int ret, back;
-       char *p;
+       int ret;
 
        td_set_runstate(td, TD_VERIFYING);
 
@@ -991,15 +1013,9 @@ static void do_async_verify(struct thread_data *td)
                if (!io_u)
                        break;
 
-               io_u->offset = td->cur_off;
-               io_u->buflen = td->max_bs;
-
-               if (io_u->offset + io_u->buflen > (td->kb << 10)) {
-                       io_u->buflen = (td->kb << 10) - io_u->offset;
-                       if (!io_u->buflen) {
-                               put_io_u(td, io_u);
-                               break;
-                       }
+               if (get_next_verify(td, &io_u->offset, &io_u->buflen)) {
+                       put_io_u(td, io_u);
+                       break;
                }
 
                io_prep_pread(&io_u->iocb, td->fd, io_u->buf, io_u->buflen, io_u->offset);
@@ -1024,24 +1040,9 @@ static void do_async_verify(struct thread_data *td)
                        break;
                }
 
-               /*
-                * got our io_u to verify, find back offset so we can
-                * submit the next one before verifying this one
-                */
                v_io_u = ev_to_iou(td->aio_events);
-               p = v_io_u->buf;
-               back = v_io_u->buflen;
-               do {
-                       hdr = (struct verify_header *) p;
-
-                       if (hdr->len > back)
-                               break;
-
-                       back -= hdr->len;
-                       p += hdr->len;
-               } while (back);
 
-               td->cur_off += (v_io_u->buflen - back);
+               td->cur_off = v_io_u->offset + v_io_u->buflen;
 
                /*
                 * if we can't submit more io, we need to verify now
@@ -1213,6 +1214,7 @@ 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++) {
@@ -1241,7 +1243,7 @@ static void finish_log(struct thread_data *td, struct io_log *log, char *name)
 {
        char file_name[128];
        FILE *f;
-       int i;
+       unsigned int i;
 
        sprintf(file_name, "client%d_%s.log", td->thread_number, name);
        f = fopen(file_name, "w");
@@ -1261,8 +1263,9 @@ static void finish_log(struct thread_data *td, struct io_log *log, char *name)
 static int create_file(struct thread_data *td)
 {
        unsigned long long left;
+       unsigned int bs;
        char *b;
-       int r, bs;
+       int r;
 
        /*
         * unless specifically asked for overwrite, let normal io extend it
@@ -1296,7 +1299,7 @@ static int create_file(struct thread_data *td)
 
                r = write(td->fd, b, bs);
 
-               if (r == bs) {
+               if (r == (int) bs) {
                        left -= bs;
                        continue;
                } else {
@@ -1655,11 +1658,11 @@ static int add_job(struct thread_data *td, const char *filename, int prioclass,
        if (td->use_aio && !td->aio_depth)
                td->aio_depth = 1;
 
-       if (td->min_bs == -1)
+       if (td->min_bs == -1U)
                td->min_bs = td->bs;
-       if (td->max_bs == -1)
+       if (td->max_bs == -1U)
                td->max_bs = td->bs;
-       if (td_read(td) || !td->sequential)
+       if (td_read(td))
                td->verify = 0;
 
        if (setup_rate(td))
diff --git a/list.h b/list.h
index 9b9541ac7301b736fecb4b99e480e9e257f104fc..3a615441cf8233d01da3dc6333bf011ac87d9f9f 100644 (file)
--- a/list.h
+++ b/list.h
@@ -59,6 +59,11 @@ static inline void list_add(struct list_head *new, struct list_head *head)
        __list_add(new, head, head->next);
 }
 
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
 /*
  * Delete a list entry by making the prev/next entries
  * point to each other.