From aea47d444b02bd7c622f82bb73151fd7136a499f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 26 May 2006 19:27:29 +0200 Subject: [PATCH] [PATCH] Support for specifying exact io pattern iolog=foo option added, when given fio will read the io pattern from it and replay it. --- README | 5 ++ fio-ini.c | 30 +++++++++-- fio.c | 145 ++++++++++++++++++++++++++++++++++++++++++++---------- fio.h | 5 ++ 4 files changed, 154 insertions(+), 31 deletions(-) diff --git a/README b/README index 3cf3f133..f6094a62 100644 --- a/README +++ b/README @@ -86,6 +86,11 @@ The 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 diff --git a/fio-ini.c b/fio-ini.c index b360e971..126e3252 100644 --- 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 8daa660b..bc008343 100644 --- 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 37dabae1..e997fbe5 100644 --- 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) \ -- 2.25.1