Recent changes (master)
The following changes since commit 8a16f59b66e5198050ec735998692ba9436a9884:
blktrace: only probe and set depth if option isn't set (2015-04-22 19:54:54 -0600)
are available in the git repository at:
git://git.kernel.dk/fio.git master
for you to fetch changes up to 36870c47801ca85d1a98c7ec47950b0aa360f865:
Merge branch 'io-threads' (2015-04-23 11:13:27 -0600)
----------------------------------------------------------------
Jens Axboe (7):
Add ->bytes_done[] to struct thread_data
backend: split queue io_u event handling into helper First cut at supporting IO offload
libaio: don't call io_destroy(), let exit_aio() take care of it Add man page and HOWTO for io_submit_mode option
Add sample job file for fixed submission rate
Merge branch 'io-threads'
HOWTO | 12 + Makefile | 2 +- backend.c | 326 ++++++++++++++------------- cconv.c | 2 + engines/libaio.c | 9 +- examples/fixed-rate-submission.fio | 10 + fio.1 | 11 + fio.h | 61 +++-- init.c | 3 + io_u.c | 113 ++++++---- ioengine.h | 15 +- ioengines.c | 16 +- libfio.c | 1 + options.c | 20 ++ stat.c | 6 + thread_options.h | 3 +- verify.c | 8 +- workqueue.c | 444 +++++++++++++++++++++++++++++++++++++ workqueue.h | 29 +++ 19 files changed, 853 insertions(+), 238 deletions(-) create mode 100644 examples/fixed-rate-submission.fio create mode 100644 workqueue.c create mode 100644 workqueue.h
---
Diff of recent changes:
HOWTO +12 -0
@@ -820,6 +820,18 @@ iodepth_low=int The low water mark indicating when to start filling after fio has filled the queue of 16 requests, it will let the depth drain down to 4 before starting to fill it again. +io_submit_mode=str This option controls how fio submits the IO to + the IO engine. The default is 'inline', which means that the + fio job threads submit and reap IO directly. If set to + 'offload', the job threads will offload IO submission to a + dedicated pool of IO threads. This requires some coordination + and thus has a bit of extra overhead, especially for lower + queue depth IO where it can increase latencies. The benefit + is that fio can manage submission rates independently of + the device completion rates. This avoids skewed latency + reporting if IO gets back up on the device side (the + coordinated omission problem). + direct=bool If value is true, use non-buffered io. This is usually O_DIRECT. Note that ZFS on Solaris doesn't support direct io. On Windows the synchronous ioengines don't support direct io.
Makefile +1 -1
@@ -36,7 +36,7 @@ SOURCE := gettime.c ioengines.c init.c stat.c log.c time.c filesetup.c \ lib/lfsr.c gettime-thread.c helpers.c lib/flist_sort.c \ lib/hweight.c lib/getrusage.c idletime.c td_error.c \ profiles/tiobench.c profiles/act.c io_u_queue.c filelock.c \ - lib/tp.c lib/bloom.c lib/gauss.c + lib/tp.c lib/bloom.c lib/gauss.c workqueue.c ifdef CONFIG_LIBHDFS HDFSFLAGS= -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/linux -I $(FIO_LIBHDFS_INCLUDE)
backend.c +166 -160
501 lines not on this page · raw message
cconv.c +2 -0
@@ -116,6 +116,7 @@ void convert_thread_options_to_cpu(struct thread_options *o, } o->ratecycle =le32_to_cpu (top->ratecycle); + o->io_submit_mode =le32_to_cpu (top->io_submit_mode); o->nr_files =le32_to_cpu (top->nr_files); o->open_files =le32_to_cpu (top->open_files); o->file_lock_mode =le32_to_cpu (top->file_lock_mode);
@@ -295,6 +296,7 @@ void convert_thread_options_to_net(struct thread_options_pack *top, top->fill_device =cpu_to_le32 (o->fill_device); top->file_append =cpu_to_le32 (o->file_append); top->ratecycle =cpu_to_le32 (o->ratecycle); + top->io_submit_mode =cpu_to_le32 (o->io_submit_mode); top->nr_files =cpu_to_le32 (o->nr_files); top->open_files =cpu_to_le32 (o->open_files); top->file_lock_mode =cpu_to_le32 (o->file_lock_mode);
engines/libaio.c +8 -1
@@ -316,7 +316,14 @@ static void fio_libaio_cleanup(struct thread_data *td)struct libaio_data *ld = td->io_ops->data;if (ld) { -io_destroy (ld->aio_ctx); +/* +* Work-around to avoid huge RCU stalls at exit time. If we +* don't do this here, then it'll be torn down by exit_aio(). +* But for that case we can parallellize the freeing, thus +* speeding it up a lot. +*/ +if (!(td->flags &TD_F_CHILD )) +io_destroy (ld->aio_ctx);free (ld->aio_events);free (ld->iocbs);free (ld->io_us);
examples/fixed-rate-submission.fio new file +10 -0
@@ -0,0 +1,10 @@ +[fixed-rate-submit] +size=128m +rw=read +ioengine=libaio +iodepth=32 +direct=1 +# by setting the submit mode to offload, we can guarantee a fixed rate of +# submission regardless of what the device completion rate is. +io_submit_mode=offload +rate_iops=1000
fio.1 +11 -0
@@ -694,6 +694,17 @@ cost of more retrieval system calls. Low watermark indicating when to start filling the queue again. Default: \fBiodepth\fR. .TP +.BI io_submit_mode \fR=\fPstr +This option controls how fio submits the IO to the IO engine. The default is +\fBinline\fR, which means that the fio job threads submit and reap IO directly. +If set to \fBoffload\fR, the job threads will offload IO submission to a +dedicated pool of IO threads. This requires some coordination and thus has a +bit of extra overhead, especially for lower queue depth IO where it can +increase latencies. The benefit is that fio can manage submission rates +independently of the device completion rates. This avoids skewed latency +reporting if IO gets back up on the device side (the coordinated omission +problem). +.TP .BI direct \fR=\fPbool If true, use non-buffered I/O (usually O_DIRECT). Default: false. .TP
fio.h +46 -15
139 lines not on this page · raw message
init.c +3 -0
@@ -956,6 +956,9 @@ static void init_flags(struct thread_data *td) td->flags |=TD_F_SCRAMBLE_BUFFERS ;if (o->verify !=VERIFY_NONE ) td->flags |=TD_F_VER_NONE ; + +if (o->verify_async || o->io_submit_mode ==IO_MODE_OFFLOAD ) + td->flags |=TD_F_NEED_LOCK ; }static int setup_random_seeds (struct thread_data *td)
io_u.c +66 -47
282 lines not on this page · raw message
ioengine.h +13 -2
@@ -119,6 +119,7 @@ struct io_u {struct ibv_mr *mr;#endif void *mmap_data; +uint64_t null; }; };
@@ -209,8 +210,8 @@ extern struct io_u *get_io_u(struct thread_data *);extern void put_io_u (struct thread_data *,struct io_u *);extern void clear_io_u (struct thread_data *,struct io_u *);extern void requeue_io_u (struct thread_data *,struct io_u **); -extern int __must_check io_u_sync_complete (struct thread_data *,struct io_u *,uint64_t *); -extern int __must_check io_u_queued_complete (struct thread_data *,int ,uint64_t *); +extern int __must_check io_u_sync_complete (struct thread_data *,struct io_u *); +extern int __must_check io_u_queued_complete (struct thread_data *,int );extern void io_u_queued (struct thread_data *,struct io_u *);extern void io_u_quiesce (struct thread_data *);extern void io_u_log_error (struct thread_data *,struct io_u *);
@@ -251,4 +252,14 @@ static inline enum fio_ddir acct_ddir(struct io_u *io_u) return io_u->ddir; } +static inline void io_u_clear (struct io_u *io_u,unsigned int flags) +{ +__sync_fetch_and_and (&io_u->flags, ~flags); +} + +static inline void io_u_set (struct io_u *io_u,unsigned int flags) +{ +__sync_fetch_and_or (&io_u->flags, flags); +} +#endif
ioengines.c +9 -7
@@ -264,13 +264,15 @@ out:int td_io_queue (struct thread_data *td,struct io_u *io_u) { +const enum fio_ddir ddir =acct_ddir (io_u); +unsigned long buflen = io_u->xfer_buflen;int ret;dprint_io_u (io_u,"queue" );fio_ro_check (td, io_u);assert ((io_u->flags &IO_U_F_FLIGHT ) ==0 ); - io_u->flags |=IO_U_F_FLIGHT ; +io_u_set (io_u,IO_U_F_FLIGHT );assert (fio_file_open (io_u->file));
@@ -294,18 +296,18 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) sizeof(struct timeval)); } -if (ddir_rw (acct_ddir (io_u))) { - td->io_issues[acct_ddir (io_u)]++; - td->io_issue_bytes[acct_ddir (io_u)] += io_u->xfer_buflen; +if (ddir_rw (ddir)) { + td->io_issues[ddir]++; + td->io_issue_bytes[ddir] += buflen; } ret = td->io_ops->queue (td, io_u);unlock_file (td, io_u->file); -if (ret ==FIO_Q_BUSY &&ddir_rw (acct_ddir (io_u))) { - td->io_issues[acct_ddir (io_u)]--; - td->io_issue_bytes[acct_ddir (io_u)] -= io_u->xfer_buflen; +if (ret ==FIO_Q_BUSY &&ddir_rw (ddir)) { + td->io_issues[ddir]--; + td->io_issue_bytes[ddir] -= buflen; }/*
libfio.c +1 -0
@@ -88,6 +88,7 @@ static void reset_io_counters(struct thread_data *td) td->this_io_blocks[ddir] =0 ; td->rate_bytes[ddir] =0 ; td->rate_blocks[ddir] =0 ; + td->bytes_done[ddir] =0 ; } td->zone_bytes =0 ;
options.c +20 -0
@@ -1623,6 +1623,26 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .group = FIO_OPT_G_IO_BASIC, }, { + .name = "io_submit_mode", + .lname = "IO submit mode", + .type = FIO_OPT_STR, + .off1 = td_var_offset(io_submit_mode), + .help = "How IO submissions and completions are done", + .def = "inline", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_IO_BASIC, + .posval = { + { .ival = "inline", + .oval = IO_MODE_INLINE, + .help = "Submit and complete IO inline", + }, + { .ival = "offload", + .oval = IO_MODE_OFFLOAD, + .help = "Offload submit and complete to threads", + }, + }, + }, + { .name = "size", .lname = "Size", .type = FIO_OPT_STR_VAL,
stat.c +6 -0
@@ -2009,6 +2009,8 @@ void add_bw_sample(struct thread_data *td, enum fio_ddir ddir, unsigned int bs,if (spent < td->o.bw_avg_time)return ; +td_io_u_lock (td); +/* * Compute both read and write rates for the interval. */
@@ -2033,6 +2035,7 @@ void add_bw_sample(struct thread_data *td, enum fio_ddir ddir, unsigned int bs, }fio_gettime (&td->bw_sample_time,NULL ); +td_io_u_unlock (td); } void add_iops_sample(struct thread_data *td, enum fio_ddir ddir, unsigned int bs,
@@ -2048,6 +2051,8 @@ void add_iops_sample(struct thread_data *td, enum fio_ddir ddir, unsigned int bsif (spent < td->o.iops_avg_time)return ; +td_io_u_lock (td); +/* * Compute both read and write rates for the interval. */
@@ -2072,6 +2077,7 @@ void add_iops_sample(struct thread_data *td, enum fio_ddir ddir, unsigned int bs }fio_gettime (&td->iops_sample_time,NULL ); +td_io_u_unlock (td); }void stat_init (void )
thread_options.h +2 -1
@@ -223,6 +223,7 @@ struct thread_options {unsigned int rate[DDIR_RWDIR_CNT ];unsigned int ratemin[DDIR_RWDIR_CNT ];unsigned int ratecycle; +unsigned int io_submit_mode;unsigned int rate_iops[DDIR_RWDIR_CNT ];unsigned int rate_iops_min[DDIR_RWDIR_CNT ];
@@ -452,6 +453,7 @@ struct thread_options_pack {uint32_t rate[DDIR_RWDIR_CNT ];uint32_t ratemin[DDIR_RWDIR_CNT ];uint32_t ratecycle; +uint32_t io_submit_mode;uint32_t rate_iops[DDIR_RWDIR_CNT ];uint32_t rate_iops_min[DDIR_RWDIR_CNT ];
@@ -489,7 +491,6 @@ struct thread_options_pack {uint64_t latency_target;uint64_t latency_window; -uint32_t pad3;fio_fp64_t latency_percentile;uint32_t block_error_hist;
verify.c +4 -4
@@ -656,7 +656,7 @@ int verify_io_u_async(struct thread_data *td, struct io_u **io_u_ptr)if (io_u->flags &IO_U_F_IN_CUR_DEPTH ) { td->cur_depth--; - io_u->flags &= ~IO_U_F_IN_CUR_DEPTH ; +io_u_clear (io_u,IO_U_F_IN_CUR_DEPTH ); }flist_add_tail (&io_u->verify_list, &td->verify_list); *io_u_ptr =NULL ;
@@ -1105,10 +1105,10 @@ int get_next_verify(struct thread_data *td, struct io_u *io_u) io_u->buflen = ipo->len; io_u->numberio = ipo->numberio; io_u->file = ipo->file; - io_u->flags |=IO_U_F_VER_LIST ; +io_u_set (io_u,IO_U_F_VER_LIST );if (ipo->flags &IP_F_TRIMMED ) - io_u->flags |=IO_U_F_TRIMMED ; +io_u_set (io_u,IO_U_F_TRIMMED );if (!fio_file_open (io_u->file)) {int r =td_io_open_file (td, io_u->file);
@@ -1192,7 +1192,7 @@ static void *verify_async_thread(void *data) io_u = flist_first_entry(&list, struct io_u, verify_list);flist_del_init (&io_u->verify_list); - io_u->flags |=IO_U_F_NO_FILE_PUT ; +io_u_set (io_u,IO_U_F_NO_FILE_PUT ); ret =verify_io_u (td, &io_u);put_io_u (td, io_u);
workqueue.c new file +444 -0
444 lines not on this page · raw message
workqueue.h new file +29 -0
@@ -0,0 +1,29 @@ +#ifndef FIO_RATE_H +#define FIO_RATE_H + +#include "flist.h" + +typedef void (workqueue_fn )(struct thread_data *,struct io_u *); + +struct workqueue { +unsigned int max_workers; + +struct thread_data *td; +workqueue_fn *fn; + +uint64_t work_seq; +struct submit_worker *workers; +unsigned int next_free_worker; + +pthread_cond_t flush_cond; +pthread_mutex_t flush_lock; +volatile int wake_idle; +}; + +int workqueue_init (struct thread_data *td,struct workqueue *wq,workqueue_fn *fn,unsigned int max_workers); +void workqueue_exit (struct workqueue *wq); + +int workqueue_enqueue (struct workqueue *wq,struct io_u *io_u); +void workqueue_flush (struct workqueue *wq); + +#endif