From: Jens Axboe Date: Fri, 3 Aug 2018 15:17:06 +0000 (-0600) Subject: Merge branch 'chunked-iolog-reading' of https://github.com/aclamk/fio X-Git-Tag: fio-3.9~49 X-Git-Url: https://git.kernel.dk/?p=fio.git;a=commitdiff_plain;h=a94aedbc11829470cce77eb52969601d618054d6;hp=a6d7953002576946bd7d6703fca698a16ad454a5 Merge branch 'chunked-iolog-reading' of https://github.com/aclamk/fio * 'chunked-iolog-reading' of https://github.com/aclamk/fio: iolog: Added new option description to HOWTO iolog: Added option read_iolog_chunked. Used to avoid reading large iologs at once. Allows iologs to be infinite, generated. --- diff --git a/HOWTO b/HOWTO index 804d93e4..16c5ae31 100644 --- a/HOWTO +++ b/HOWTO @@ -2327,6 +2327,12 @@ I/O replay replay, the file needs to be turned into a blkparse binary data file first (``blkparse -o /dev/null -d file_for_fio.bin``). +.. option:: read_iolog_chunked=bool + + Determines how iolog is read. If false(default) entire :option:`read_iolog` + will be read at once. If selected true, input from iolog will be read + gradually. Useful when iolog is very large, or it is generated. + .. option:: replay_no_stall=bool When replaying I/O with :option:`read_iolog` the default behavior is to diff --git a/backend.c b/backend.c index 3c45e789..4b4ecdef 100644 --- a/backend.c +++ b/backend.c @@ -966,8 +966,10 @@ static void do_io(struct thread_data *td, uint64_t *bytes_done) * Break if we exceeded the bytes. The exception is time * based runs, but we still need to break out of the loop * for those to run verification, if enabled. + * Jobs read from iolog do not use this stop condition. */ if (bytes_issued >= total_bytes && + !td->o.read_iolog_file && (!td->o.time_based || (td->o.time_based && td->o.verify != VERIFY_NONE))) break; @@ -1909,6 +1911,8 @@ err: */ if (o->write_iolog_file) write_iolog_close(td); + if (td->io_log_rfile) + fclose(td->io_log_rfile); td_set_runstate(td, TD_EXITED); diff --git a/fio.1 b/fio.1 index a446aba5..4386f857 100644 --- a/fio.1 +++ b/fio.1 @@ -2057,6 +2057,11 @@ to replay a workload captured by blktrace. See replay, the file needs to be turned into a blkparse binary data file first (`blkparse \-o /dev/null \-d file_for_fio.bin'). .TP +.BI read_iolog_chunked \fR=\fPbool +Determines how iolog is read. If false (default) entire \fBread_iolog\fR will +be read at once. If selected true, input from iolog will be read gradually. +Useful when iolog is very large, or it is generated. +.TP .BI replay_no_stall \fR=\fPbool When replaying I/O with \fBread_iolog\fR the default behavior is to attempt to respect the timestamps within the log and replay them with the diff --git a/fio.h b/fio.h index 685aab19..b58057f7 100644 --- a/fio.h +++ b/fio.h @@ -399,6 +399,11 @@ struct thread_data { * For IO replaying */ struct flist_head io_log_list; + FILE *io_log_rfile; + unsigned int io_log_current; + unsigned int io_log_checkmark; + unsigned int io_log_highmark; + struct timespec io_log_highmark_time; /* * For tracking/handling discards diff --git a/iolog.c b/iolog.c index eb38027e..b0122bed 100644 --- a/iolog.c +++ b/iolog.c @@ -141,6 +141,8 @@ static int ipo_special(struct thread_data *td, struct io_piece *ipo) return 1; } +static bool read_iolog2(struct thread_data *td); + int read_iolog_get(struct thread_data *td, struct io_u *io_u) { struct io_piece *ipo; @@ -148,7 +150,13 @@ int read_iolog_get(struct thread_data *td, struct io_u *io_u) while (!flist_empty(&td->io_log_list)) { int ret; - + if (td->o.read_iolog_chunked) { + if (td->io_log_checkmark == td->io_log_current) { + if (!read_iolog2(td)) + return 1; + } + td->io_log_current--; + } ipo = flist_first_entry(&td->io_log_list, struct io_piece, list); flist_del(&ipo->list); remove_trim_entry(td, ipo); @@ -345,7 +353,7 @@ void write_iolog_close(struct thread_data *td) * Read version 2 iolog data. It is enhanced to include per-file logging, * syncs, etc. */ -static bool read_iolog2(struct thread_data *td, FILE *f) +static bool read_iolog2(struct thread_data *td) { unsigned long long offset; unsigned int bytes; @@ -353,9 +361,28 @@ static bool read_iolog2(struct thread_data *td, FILE *f) char *rfname, *fname, *act; char *str, *p; enum fio_ddir rw; + int64_t items_to_fetch = 0; - free_release_files(td); - + if (td->o.read_iolog_chunked) { + if (td->io_log_highmark == 0) { + items_to_fetch = 10; + } else { + struct timespec now; + uint64_t elapsed; + uint64_t for_1s; + fio_gettime(&now, NULL); + elapsed = ntime_since(&td->io_log_highmark_time, &now); + for_1s = (td->io_log_highmark - td->io_log_current) * 1000000000 / elapsed; + items_to_fetch = for_1s - td->io_log_current; + if (items_to_fetch < 0) + items_to_fetch = 0; + td->io_log_highmark = td->io_log_current + items_to_fetch; + td->io_log_checkmark = (td->io_log_highmark + 1) / 2; + fio_gettime(&td->io_log_highmark_time, NULL); + if (items_to_fetch == 0) + return true; + } + } /* * Read in the read iolog and store it, reuse the infrastructure * for doing verifications. @@ -365,7 +392,7 @@ static bool read_iolog2(struct thread_data *td, FILE *f) act = malloc(256+16); reads = writes = waits = 0; - while ((p = fgets(str, 4096, f)) != NULL) { + while ((p = fgets(str, 4096, td->io_log_rfile)) != NULL) { struct io_piece *ipo; int r; @@ -468,18 +495,39 @@ static bool read_iolog2(struct thread_data *td, FILE *f) } queue_io_piece(td, ipo); + + if (td->o.read_iolog_chunked) { + td->io_log_current++; + items_to_fetch--; + if (items_to_fetch == 0) + break; + } } free(str); free(act); free(rfname); + if (td->o.read_iolog_chunked) { + td->io_log_highmark = td->io_log_current; + td->io_log_checkmark = (td->io_log_highmark + 1) / 2; + fio_gettime(&td->io_log_highmark_time, NULL); + } + if (writes && read_only) { log_err("fio: <%s> skips replay of %d writes due to" " read-only\n", td->o.name, writes); writes = 0; } + if (td->o.read_iolog_chunked) { + if (td->io_log_current == 0) { + return false; + } + td->o.td_ddir = TD_DDIR_RW; + return true; + } + if (!reads && !writes && !waits) return false; else if (reads && !writes) @@ -544,19 +592,20 @@ static bool init_iolog_read(struct thread_data *td) fclose(f); return false; } - + td->io_log_rfile = f; /* * version 2 of the iolog stores a specific string as the * first line, check for that */ - if (!strncmp(iolog_ver2, buffer, strlen(iolog_ver2))) - ret = read_iolog2(td, f); + if (!strncmp(iolog_ver2, buffer, strlen(iolog_ver2))) { + free_release_files(td); + ret = read_iolog2(td); + } else { log_err("fio: iolog version 1 is no longer supported\n"); ret = false; } - fclose(f); return ret; } diff --git a/options.c b/options.c index 4b464028..f5920278 100644 --- a/options.c +++ b/options.c @@ -3134,6 +3134,17 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IOLOG, }, + { + .name = "read_iolog_chunked", + .lname = "Read I/O log in parts", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct thread_options, read_iolog_chunked), + .def = "0", + .parent = "read_iolog", + .help = "Parse IO pattern in chunks", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_IOLOG, + }, { .name = "replay_no_stall", .lname = "Don't stall on replay", diff --git a/thread_options.h b/thread_options.h index 8adba48c..8bbf54bf 100644 --- a/thread_options.h +++ b/thread_options.h @@ -247,6 +247,7 @@ struct thread_options { fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN]; char *read_iolog_file; + bool read_iolog_chunked; char *write_iolog_file; unsigned int write_bw_log;