From 046395d7ab181288d14737c1d0041e98328f473f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 9 Apr 2014 13:57:38 -0600 Subject: [PATCH] Add exit_on_io_done option to the CPU IO engine The CPU IO engine is most often used to saturate the system, while running an IO load on it. As such, it's useful to have CPU engine threads exit automatically, when IO has completed. Add exit_on_io_done as a CPU IO engine option for that purpose. Signed-off-by: Jens Axboe --- HOWTO | 2 ++ engines/cpu.c | 16 ++++++++++++++++ fio.1 | 11 +++-------- fio.h | 2 ++ ioengines.c | 3 +++ libfio.c | 15 +++++++++++++++ 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/HOWTO b/HOWTO index 7db7b892..f74360de 100644 --- a/HOWTO +++ b/HOWTO @@ -1505,6 +1505,8 @@ that defines them is selected. [cpu] cpuchunks=int Split the load into cycles of the given time. In microseconds. +[cpu] exit_on_io_done=bool Detect when IO threads are done, then exit. + [netsplice] hostname=str [net] hostname=str The host name or IP address to use for TCP or UDP based IO. If the job is a TCP listener or UDP reader, the hostname is not diff --git a/engines/cpu.c b/engines/cpu.c index c798f188..85598ef7 100644 --- a/engines/cpu.c +++ b/engines/cpu.c @@ -11,6 +11,7 @@ struct cpu_options { struct thread_data *td; unsigned int cpuload; unsigned int cpucycle; + unsigned int exit_io_done; }; static struct fio_option options[] = { @@ -35,6 +36,16 @@ static struct fio_option options[] = { .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_INVALID, }, + { + .name = "exit_on_io_done", + .lname = "Exit when IO threads are done", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct cpu_options, exit_io_done), + .help = "Exit when IO threads finish", + .def = "0", + .category = FIO_OPT_C_GENERAL, + .group = FIO_OPT_G_INVALID, + }, { .name = NULL, }, @@ -45,6 +56,11 @@ static int fio_cpuio_queue(struct thread_data *td, struct io_u fio_unused *io_u) { struct cpu_options *co = td->eo; + if (co->exit_io_done && !fio_running_or_pending_io_threads()) { + td->done = 1; + return FIO_Q_BUSY; + } + usec_spin(co->cpucycle); return FIO_Q_COMPLETED; } diff --git a/fio.1 b/fio.1 index 91f96e02..8cf37780 100644 --- a/fio.1 +++ b/fio.1 @@ -1226,14 +1226,6 @@ Output is redirected in a file called \fBjobname.postrun.txt\fR .BI ioscheduler \fR=\fPstr Attempt to switch the device hosting the file to the specified I/O scheduler. .TP -.BI cpuload \fR=\fPint -If the job is a CPU cycle-eater, attempt to use the specified percentage of -CPU cycles. -.TP -.BI cpuchunks \fR=\fPint -If the job is a CPU cycle-eater, split the load into cycles of the -given time in milliseconds. -.TP .BI disk_util \fR=\fPbool Generate disk utilization statistics if the platform supports it. Default: true. .TP @@ -1375,6 +1367,9 @@ Attempt to use the specified percentage of CPU cycles. .BI (cpu)cpuchunks \fR=\fPint Split the load into cycles of the given time. In microseconds. .TP +.BI (cpu)exit_on_io_done \fR=\fPbool +Detect when IO threads are done, then exit. +.TP .BI (libaio)userspace_reap Normally, with the libaio engine in use, fio will use the io_getevents system call to reap newly returned events. diff --git a/fio.h b/fio.h index a539f21b..3df5bd9f 100644 --- a/fio.h +++ b/fio.h @@ -72,6 +72,7 @@ enum { TD_F_VER_NONE = 32, TD_F_PROFILE_OPS = 64, TD_F_COMPRESS = 128, + TD_F_NOIO = 256, }; enum { @@ -439,6 +440,7 @@ extern void add_job_opts(const char **, int); extern char *num2str(unsigned long, int, int, int, int); extern int ioengine_load(struct thread_data *); extern int parse_dryrun(void); +extern int fio_running_or_pending_io_threads(void); extern uintptr_t page_mask; extern uintptr_t page_size; diff --git a/ioengines.c b/ioengines.c index 3c75fa6b..0f94d0d9 100644 --- a/ioengines.c +++ b/ioengines.c @@ -375,6 +375,9 @@ int td_io_init(struct thread_data *td) td->error = ret; } + if (!ret && (td->io_ops->flags & FIO_NOIO)) + td->flags |= TD_F_NOIO; + return ret; } diff --git a/libfio.c b/libfio.c index 1fd77e40..3fde492c 100644 --- a/libfio.c +++ b/libfio.c @@ -218,6 +218,21 @@ void fio_terminate_threads(int group_id) } } +int fio_running_or_pending_io_threads(void) +{ + struct thread_data *td; + int i; + + for_each_td(td, i) { + if (td->flags & TD_F_NOIO) + continue; + if (td->runstate < TD_EXITED) + return 1; + } + + return 0; +} + static int endian_check(void) { union { -- 2.25.1