From 645785e5ceb84bda7b74cc17d8db13840aed6d2f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 4 Nov 2005 13:39:32 +0100 Subject: [PATCH] [PATCH] fio: make random io writes verifiable as well We maintain a log of successfully written blocks, so we can just unwind it for verification. --- fio.c | 201 +++++++++++++++++++++++++++++---------------------------- list.h | 5 ++ 2 files changed, 107 insertions(+), 99 deletions(-) diff --git a/fio.c b/fio.c index bfc3680..d5c65dd 100644 --- 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 9b9541a..3a61544 100644 --- 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. -- 2.25.1