X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=io_u.c;h=3eeade2647a19a260d6a5a84571704699481be18;hp=41b5cdbb04894192cb06e2d86a49bdf28d48be9d;hb=8e239cae8aae89f07a885ffcc985600ce9c65d5d;hpb=5f9099ea8adf423d0db01274ef0d7e65629c0e1c diff --git a/io_u.c b/io_u.c index 41b5cdbb..3eeade26 100644 --- a/io_u.c +++ b/io_u.c @@ -8,6 +8,7 @@ #include "fio.h" #include "hash.h" #include "verify.h" +#include "lib/rand.h" struct io_completion_data { int nr; /* input */ @@ -40,10 +41,12 @@ static void mark_random_map(struct thread_data *td, struct io_u *io_u) struct fio_file *f = io_u->file; unsigned long long block; unsigned int blocks, nr_blocks; + int busy_check; block = (io_u->offset - f->file_offset) / (unsigned long long) min_bs; nr_blocks = (io_u->buflen + min_bs - 1) / min_bs; blocks = 0; + busy_check = !(io_u->flags & IO_U_F_BUSY_OK); while (nr_blocks) { unsigned int this_blocks, mask; @@ -53,11 +56,12 @@ static void mark_random_map(struct thread_data *td, struct io_u *io_u) * If we have a mixed random workload, we may * encounter blocks we already did IO to. */ - if ((td->o.ddir_nr == 1) && !random_map_free(f, block)) { - if (!blocks) - blocks = 1; + if (!busy_check) { + blocks = nr_blocks; break; } + if ((td->o.ddir_seq_nr == 1) && !random_map_free(f, block)) + break; idx = RAND_MAP_IDX(f, block); bit = RAND_MAP_BIT(f, block); @@ -68,10 +72,20 @@ static void mark_random_map(struct thread_data *td, struct io_u *io_u) if (this_blocks + bit > BLOCKS_PER_MAP) this_blocks = BLOCKS_PER_MAP - bit; - if (this_blocks == BLOCKS_PER_MAP) - mask = -1U; - else - mask = ((1U << this_blocks) - 1) << bit; + do { + if (this_blocks == BLOCKS_PER_MAP) + mask = -1U; + else + mask = ((1U << this_blocks) - 1) << bit; + + if (!(f->file_map[idx] & mask)) + break; + + this_blocks--; + } while (this_blocks); + + if (!this_blocks) + break; f->file_map[idx] |= mask; nr_blocks -= this_blocks; @@ -182,36 +196,82 @@ static int get_next_rand_offset(struct thread_data *td, struct fio_file *f, return get_next_free_block(td, f, ddir, b); } +static int get_next_rand_block(struct thread_data *td, struct fio_file *f, + enum fio_ddir ddir, unsigned long long *b) +{ + if (get_next_rand_offset(td, f, ddir, b)) { + dprint(FD_IO, "%s: rand offset failed, last=%llu, size=%llu\n", + f->file_name, f->last_pos, f->real_file_size); + return 1; + } + + return 0; +} + +static int get_next_seq_block(struct thread_data *td, struct fio_file *f, + enum fio_ddir ddir, unsigned long long *b) +{ + if (f->last_pos < f->real_file_size) { + *b = (f->last_pos - f->file_offset) / td->o.min_bs[ddir]; + return 0; + } + + return 1; +} + +static int get_next_block(struct thread_data *td, struct io_u *io_u, + enum fio_ddir ddir, int rw_seq, unsigned long long *b) +{ + struct fio_file *f = io_u->file; + int ret; + + if (rw_seq) { + if (td_random(td)) + ret = get_next_rand_block(td, f, ddir, b); + else + ret = get_next_seq_block(td, f, ddir, b); + } else { + io_u->flags |= IO_U_F_BUSY_OK; + + if (td->o.rw_seq == RW_SEQ_SEQ) { + ret = get_next_seq_block(td, f, ddir, b); + if (ret) + ret = get_next_rand_block(td, f, ddir, b); + } else if (td->o.rw_seq == RW_SEQ_IDENT) { + if (f->last_start != -1ULL) + *b = (f->last_start - f->file_offset) / td->o.min_bs[ddir]; + else + *b = 0; + ret = 0; + } else { + log_err("fio: unknown rw_seq=%d\n", td->o.rw_seq); + ret = 1; + } + } + + return ret; +} + /* * For random io, generate a random new block and see if it's used. Repeat * until we find a free one. For sequential io, just return the end of * the last io issued. */ -static int get_next_offset(struct thread_data *td, struct io_u *io_u) +static int __get_next_offset(struct thread_data *td, struct io_u *io_u) { struct fio_file *f = io_u->file; unsigned long long b; enum fio_ddir ddir = io_u->ddir; + int rw_seq_hit = 0; - if (td_random(td) && (td->o.ddir_nr && !--td->ddir_nr)) { - td->ddir_nr = td->o.ddir_nr; + if (td->o.ddir_seq_nr && !--td->ddir_seq_nr) { + rw_seq_hit = 1; + td->ddir_seq_nr = td->o.ddir_seq_nr; + } - if (get_next_rand_offset(td, f, ddir, &b)) { - dprint(FD_IO, "%s: getting rand offset failed\n", - f->file_name); - return 1; - } - } else { - if (f->last_pos >= f->real_file_size) { - if (!td_random(td) || - get_next_rand_offset(td, f, ddir, &b)) { - dprint(FD_IO, "%s: pos %llu > size %llu\n", - f->file_name, f->last_pos, - f->real_file_size); - return 1; - } - } else - b = (f->last_pos - f->file_offset) / td->o.min_bs[ddir]; + if (get_next_block(td, io_u, ddir, rw_seq_hit, &b)) { + printf("fail\n"); + return 1; } io_u->offset = b * td->o.ba[ddir]; @@ -231,12 +291,17 @@ static int get_next_offset(struct thread_data *td, struct io_u *io_u) return 0; } -static inline int is_power_of_2(unsigned int val) +static int get_next_offset(struct thread_data *td, struct io_u *io_u) { - return (val != 0 && ((val & (val - 1)) == 0)); + struct prof_io_ops *ops = &td->prof_io_ops; + + if (ops->fill_io_u_off) + return ops->fill_io_u_off(td, io_u); + + return __get_next_offset(td, io_u); } -static unsigned int get_next_buflen(struct thread_data *td, struct io_u *io_u) +static unsigned int __get_next_buflen(struct thread_data *td, struct io_u *io_u) { const int ddir = io_u->ddir; unsigned int uninitialized_var(buflen); @@ -281,6 +346,16 @@ static unsigned int get_next_buflen(struct thread_data *td, struct io_u *io_u) return buflen; } +static unsigned int get_next_buflen(struct thread_data *td, struct io_u *io_u) +{ + struct prof_io_ops *ops = &td->prof_io_ops; + + if (ops->fill_io_u_size) + return ops->fill_io_u_size(td, io_u); + + return __get_next_buflen(td, io_u); +} + static void set_rwmix_bytes(struct thread_data *td) { unsigned int diff; @@ -350,7 +425,7 @@ static enum fio_ddir rate_ddir(struct thread_data *td, enum fio_ddir ddir) odir = ddir ^ 1; if (td_rw(td) && __should_check_rate(td, odir)) td->rate_pending_usleep[odir] -= usec; - + return ddir; } @@ -379,6 +454,14 @@ static enum fio_ddir get_rw_ddir(struct thread_data *td) td->io_issues[DDIR_WRITE] && should_fsync(td)) return DDIR_DATASYNC; + /* + * see if it's time to sync_file_range + */ + if (td->sync_file_range_nr && + !(td->io_issues[DDIR_WRITE] % td->sync_file_range_nr) && + td->io_issues[DDIR_WRITE] && should_fsync(td)) + return DDIR_SYNC_FILE_RANGE; + if (td_rw(td)) { /* * Check if it's time to seed a new data direction. @@ -406,7 +489,7 @@ static enum fio_ddir get_rw_ddir(struct thread_data *td) return td->rwmix_ddir; } -static void put_file_log(struct thread_data *td, struct fio_file *f) +void put_file_log(struct thread_data *td, struct fio_file *f) { int ret = put_file(td, f); @@ -416,16 +499,21 @@ static void put_file_log(struct thread_data *td, struct fio_file *f) void put_io_u(struct thread_data *td, struct io_u *io_u) { - assert((io_u->flags & IO_U_F_FREE) == 0); + td_io_u_lock(td); + io_u->flags |= IO_U_F_FREE; + io_u->flags &= ~IO_U_F_FREE_DEF; if (io_u->file) put_file_log(td, io_u->file); io_u->file = NULL; - flist_del(&io_u->list); + if (io_u->flags & IO_U_F_IN_CUR_DEPTH) + td->cur_depth--; + flist_del_init(&io_u->list); flist_add(&io_u->list, &td->io_u_freelist); - td->cur_depth--; + td_io_u_unlock(td); + td_io_u_free_notify(td); } void clear_io_u(struct thread_data *td, struct io_u *io_u) @@ -440,15 +528,18 @@ void requeue_io_u(struct thread_data *td, struct io_u **io_u) dprint(FD_IO, "requeue %p\n", __io_u); + td_io_u_lock(td); + __io_u->flags |= IO_U_F_FREE; if ((__io_u->flags & IO_U_F_FLIGHT) && !ddir_sync(__io_u->ddir)) td->io_issues[__io_u->ddir]--; __io_u->flags &= ~IO_U_F_FLIGHT; - + if (__io_u->flags & IO_U_F_IN_CUR_DEPTH) + td->cur_depth--; flist_del(&__io_u->list); flist_add_tail(&__io_u->list, &td->io_u_requeues); - td->cur_depth--; + td_io_u_unlock(td); *io_u = NULL; } @@ -751,7 +842,8 @@ static struct fio_file *get_next_file_rr(struct thread_data *td, int goodf, opened = 1; } - dprint(FD_FILE, "goodf=%x, badf=%x, ff=%x\n", goodf, badf, f->flags); + dprint(FD_FILE, "goodf=%x, badf=%x, ff=%x\n", goodf, badf, + f->flags); if ((!goodf || (f->flags & goodf)) && !(f->flags & badf)) break; @@ -765,7 +857,7 @@ static struct fio_file *get_next_file_rr(struct thread_data *td, int goodf, return f; } -static struct fio_file *get_next_file(struct thread_data *td) +static struct fio_file *__get_next_file(struct thread_data *td) { struct fio_file *f; @@ -800,6 +892,16 @@ out: return f; } +static struct fio_file *get_next_file(struct thread_data *td) +{ + struct prof_io_ops *ops = &td->prof_io_ops; + + if (ops->get_next_file) + return ops->get_next_file(td); + + return __get_next_file(td); +} + static int set_io_u_file(struct thread_data *td, struct io_u *io_u) { struct fio_file *f; @@ -820,7 +922,8 @@ static int set_io_u_file(struct thread_data *td, struct io_u *io_u) io_u->file = NULL; fio_file_set_done(f); td->nr_done_files++; - dprint(FD_FILE, "%s: is done (%d of %d)\n", f->file_name, td->nr_done_files, td->o.nr_files); + dprint(FD_FILE, "%s: is done (%d of %d)\n", f->file_name, + td->nr_done_files, td->o.nr_files); } while (1); return 0; @@ -831,6 +934,9 @@ struct io_u *__get_io_u(struct thread_data *td) { struct io_u *io_u = NULL; + td_io_u_lock(td); + +again: if (!flist_empty(&td->io_u_requeues)) io_u = flist_entry(td->io_u_requeues.next, struct io_u, list); else if (!queue_full(td)) { @@ -844,14 +950,23 @@ struct io_u *__get_io_u(struct thread_data *td) if (io_u) { assert(io_u->flags & IO_U_F_FREE); - io_u->flags &= ~IO_U_F_FREE; + io_u->flags &= ~(IO_U_F_FREE | IO_U_F_FREE_DEF); io_u->error = 0; flist_del(&io_u->list); flist_add(&io_u->list, &td->io_u_busylist); td->cur_depth++; + io_u->flags |= IO_U_F_IN_CUR_DEPTH; + } else if (td->o.verify_async) { + /* + * We ran out, wait for async verify threads to finish and + * return one + */ + pthread_cond_wait(&td->free_cond, &td->io_u_lock); + goto again; } + td_io_u_unlock(td); return io_u; } @@ -870,6 +985,24 @@ struct io_u *get_io_u(struct thread_data *td) return NULL; } + if (td->o.verify_backlog && td->io_hist_len) { + int get_verify = 0; + + if (td->verify_batch) { + td->verify_batch--; + get_verify = 1; + } else if (!(td->io_hist_len % td->o.verify_backlog) && + td->last_ddir != DDIR_READ) { + td->verify_batch = td->o.verify_batch; + if (!td->verify_batch) + td->verify_batch = td->o.verify_backlog; + get_verify = 1; + } + + if (get_verify && !get_next_verify(td, io_u)) + goto out; + } + /* * from a requeue, io_u already setup */ @@ -896,12 +1029,20 @@ struct io_u *get_io_u(struct thread_data *td) goto err_put; } + f->last_start = io_u->offset; f->last_pos = io_u->offset + io_u->buflen; if (td->o.verify != VERIFY_NONE && io_u->ddir == DDIR_WRITE) populate_verify_io_u(td, io_u); else if (td->o.refill_buffers && io_u->ddir == DDIR_WRITE) io_u_fill_buffer(td, io_u, io_u->xfer_buflen); + else if (io_u->ddir == DDIR_READ) { + /* + * Reset the buf_filled parameters so next time if the + * buffer is used for writes it is refilled. + */ + io_u->buf_filled_len = 0; + } } /* @@ -948,52 +1089,80 @@ static void io_completed(struct thread_data *td, struct io_u *io_u, * initialized, silence that warning. */ unsigned long uninitialized_var(usec); + struct fio_file *f; dprint_io_u(io_u, "io complete"); + td_io_u_lock(td); assert(io_u->flags & IO_U_F_FLIGHT); - io_u->flags &= ~IO_U_F_FLIGHT; + io_u->flags &= ~(IO_U_F_FLIGHT | IO_U_F_BUSY_OK); + td_io_u_unlock(td); if (ddir_sync(io_u->ddir)) { td->last_was_sync = 1; + f = io_u->file; + if (f) { + f->first_write = -1ULL; + f->last_write = -1ULL; + } return; } td->last_was_sync = 0; + td->last_ddir = io_u->ddir; if (!io_u->error) { unsigned int bytes = io_u->buflen - io_u->resid; const enum fio_ddir idx = io_u->ddir; + const enum fio_ddir odx = io_u->ddir ^ 1; int ret; td->io_blocks[idx]++; td->io_bytes[idx] += bytes; td->this_io_bytes[idx] += bytes; + if (idx == DDIR_WRITE) { + f = io_u->file; + if (f) { + if (f->first_write == -1ULL || + io_u->offset < f->first_write) + f->first_write = io_u->offset; + if (f->last_write == -1ULL || + ((io_u->offset + bytes) > f->last_write)) + f->last_write = io_u->offset + bytes; + } + } + if (ramp_time_over(td)) { unsigned long uninitialized_var(lusec); - unsigned long uninitialized_var(rusec); if (!td->o.disable_clat || !td->o.disable_bw) lusec = utime_since(&io_u->issue_time, &icd->time); - if (__should_check_rate(td, idx) || - __should_check_rate(td, idx ^ 1)) - rusec = utime_since(&io_u->start_time, - &icd->time); + if (!td->o.disable_lat) { + unsigned long tusec; + tusec = utime_since(&io_u->start_time, + &icd->time); + add_lat_sample(td, idx, tusec, bytes); + } if (!td->o.disable_clat) { - add_clat_sample(td, idx, usec, bytes); + add_clat_sample(td, idx, lusec, bytes); io_u_mark_latency(td, lusec); } if (!td->o.disable_bw) add_bw_sample(td, idx, bytes, &icd->time); if (__should_check_rate(td, idx)) { - td->rate_pending_usleep[idx] += - (long) td->rate_usec_cycle[idx] - rusec; + td->rate_pending_usleep[idx] = + ((td->this_io_bytes[idx] * + td->rate_nsec_cycle[idx]) / 1000 - + utime_since_now(&td->start)); } if (__should_check_rate(td, idx ^ 1)) - td->rate_pending_usleep[idx ^ 1] -= rusec; + td->rate_pending_usleep[odx] = + ((td->this_io_bytes[odx] * + td->rate_nsec_cycle[odx]) / 1000 - + utime_since_now(&td->start)); } if (td_write(td) && idx == DDIR_WRITE && @@ -1047,7 +1216,9 @@ static void ios_completed(struct thread_data *td, io_u = td->io_ops->event(td, i); io_completed(td, io_u, icd); - put_io_u(td, io_u); + + if (!(io_u->flags & IO_U_F_FREE_DEF)) + put_io_u(td, io_u); } } @@ -1061,7 +1232,9 @@ int io_u_sync_complete(struct thread_data *td, struct io_u *io_u, init_icd(td, &icd, 1); io_completed(td, io_u, &icd); - put_io_u(td, io_u); + + if (!(io_u->flags & IO_U_F_FREE_DEF)) + put_io_u(td, io_u); if (icd.error) { td_verror(td, icd.error, "io_u_sync_complete"); @@ -1133,13 +1306,8 @@ void io_u_queued(struct thread_data *td, struct io_u *io_u) void io_u_fill_buffer(struct thread_data *td, struct io_u *io_u, unsigned int max_bs) { - long *ptr = io_u->buf; - - if (!td->o.zero_buffers) { - while ((void *) ptr - io_u->buf < max_bs) { - *ptr = rand() * GOLDEN_RATIO_PRIME; - ptr++; - } - } else - memset(ptr, 0, max_bs); + if (!td->o.zero_buffers) + fill_random_buf(io_u->buf, max_bs); + else + memset(io_u->buf, 0, max_bs); }