The following changes since commit c9beb194ef608dc49dd2f7537b90951f3c8b432d: t/run-fio-tests.py: Accept a wider range of IOPS values (2020-06-07 17:30:42 -0600) are available in the Git repository at: git://git.kernel.dk/fio.git master for you to fetch changes up to 14060ebb90ce5a0a164d0e5e52c13e31b53b282d: Merge branch 'latency_window' of https://github.com/liu-song-6/fio (2020-06-09 19:43:52 -0600) ---------------------------------------------------------------- Bart Van Assche (5): Make json_object_add_value_string() duplicate its 'value' argument client: Fix two memory leaks in handle_job_opt() client: Make skipping option appending in handle_job_opt() more selective client: Fix a memory leak in an error path client: Fix another memory leak in an error path Jens Axboe (4): Merge branch 'client-leak-fix' of https://github.com/bvanassche/fio Merge branch 'master' of https://github.com/bvanassche/fio Merge branch 'nowait' of https://github.com/koct9i/fio Merge branch 'latency_window' of https://github.com/liu-song-6/fio Konstantin Khlebnikov (1): engines: pvsync2 libaio io_uring: add support for RWF_NOWAIT Song Liu (1): init: fix unit of latency_window HOWTO | 20 ++++++++++++++++++++ client.c | 37 ++++++++++++++----------------------- configure | 20 ++++++++++++++++++++ engines/io_uring.c | 12 ++++++++++++ engines/libaio.c | 22 +++++++++++++++++++++- engines/sync.c | 12 ++++++++++++ fio.1 | 16 ++++++++++++++++ init.c | 1 - json.h | 2 +- os/os-linux.h | 3 +++ stat.c | 6 +----- 11 files changed, 120 insertions(+), 31 deletions(-) --- Diff of recent changes: diff --git a/HOWTO b/HOWTO index 9e71a619..9e339bb8 100644 --- a/HOWTO +++ b/HOWTO @@ -2097,6 +2097,26 @@ with the caveat that when used on the command line, they must come after the When hipri is set this determines the probability of a pvsync2 I/O being high priority. The default is 100%. +.. option:: nowait : [pvsync2] [libaio] [io_uring] + + By default if a request cannot be executed immediately (e.g. resource starvation, + waiting on locks) it is queued and the initiating process will be blocked until + the required resource becomes free. + + This option sets the RWF_NOWAIT flag (supported from the 4.14 Linux kernel) and + the call will return instantly with EAGAIN or a partial result rather than waiting. + + It is useful to also use ignore_error=EAGAIN when using this option. + + Note: glibc 2.27, 2.28 have a bug in syscall wrappers preadv2, pwritev2. + They return EOPNOTSUP instead of EAGAIN. + + For cached I/O, using this option usually means a request operates only with + cached data. Currently the RWF_NOWAIT flag does not supported for cached write. + + For direct I/O, requests will only succeed if cache invalidation isn't required, + file blocks are fully allocated and the disk request could be issued immediately. + .. option:: cpuload=int : [cpuio] Attempt to use the specified percentage of CPU cycles. This is a mandatory diff --git a/client.c b/client.c index b7575596..29d8750a 100644 --- a/client.c +++ b/client.c @@ -390,8 +390,6 @@ struct fio_client *fio_client_add_explicit(struct client_ops *ops, client = get_new_client(); - client->hostname = strdup(hostname); - if (type == Fio_client_socket) client->is_sock = true; else { @@ -410,6 +408,7 @@ struct fio_client *fio_client_add_explicit(struct client_ops *ops, client->ops = ops; client->refs = 1; client->type = ops->client_type; + client->hostname = strdup(hostname); __fio_client_add_cmd_option(client, "fio"); @@ -471,8 +470,10 @@ int fio_client_add(struct client_ops *ops, const char *hostname, void **cookie) &client->is_sock, &client->port, &client->addr.sin_addr, &client->addr6.sin6_addr, - &client->ipv6)) + &client->ipv6)) { + fio_put_client(client); return -1; + } client->fd = -1; client->ops = ops; @@ -1140,37 +1141,27 @@ static void handle_gs(struct fio_client *client, struct fio_net_cmd *cmd) static void handle_job_opt(struct fio_client *client, struct fio_net_cmd *cmd) { struct cmd_job_option *pdu = (struct cmd_job_option *) cmd->payload; - struct print_option *p; - - if (!job_opt_object) - return; pdu->global = le16_to_cpu(pdu->global); pdu->truncated = le16_to_cpu(pdu->truncated); pdu->groupid = le32_to_cpu(pdu->groupid); - p = malloc(sizeof(*p)); - p->name = strdup((char *) pdu->name); - if (pdu->value[0] != '\0') - p->value = strdup((char *) pdu->value); - else - p->value = NULL; - if (pdu->global) { - const char *pos = ""; + if (!job_opt_object) + return; - if (p->value) - pos = p->value; - - json_object_add_value_string(job_opt_object, p->name, pos); + json_object_add_value_string(job_opt_object, + (const char *)pdu->name, + (const char *)pdu->value); } else if (client->opt_lists) { struct flist_head *opt_list = &client->opt_lists[pdu->groupid]; + struct print_option *p; + p = malloc(sizeof(*p)); + p->name = strdup((const char *)pdu->name); + p->value = pdu->value[0] ? strdup((const char *)pdu->value) : + NULL; flist_add_tail(&p->list, opt_list); - } else { - free(p->value); - free(p->name); - free(p); } } diff --git a/configure b/configure index cf8b88e4..3ee8aaf2 100755 --- a/configure +++ b/configure @@ -617,8 +617,25 @@ EOF libaio=no libaio_uring=no fi + + cat > $TMPC <<EOF +#include <libaio.h> +#include <stddef.h> +int main(void) +{ + io_prep_preadv2(NULL, 0, NULL, 0, 0, 0); + io_prep_pwritev2(NULL, 0, NULL, 0, 0, 0); + return 0; +} +EOF + if compile_prog "" "" "libaio rw flags" ; then + libaio_rw_flags=yes + else + libaio_rw_flags=no + fi fi print_config "Linux AIO support" "$libaio" +print_config "Linux AIO support rw flags" "$libaio_rw_flags" print_config "Linux AIO over io_uring" "$libaio_uring" ########################################## @@ -2646,6 +2663,9 @@ if test "$zlib" = "yes" ; then fi if test "$libaio" = "yes" ; then output_sym "CONFIG_LIBAIO" + if test "$libaio_rw_flags" = "yes" ; then + output_sym "CONFIG_LIBAIO_RW_FLAGS" + fi if test "$libaio_uring" = "yes" ; then output_sym "CONFIG_LIBAIO_URING" fi diff --git a/engines/io_uring.c b/engines/io_uring.c index ac57af8f..cab7ecaf 100644 --- a/engines/io_uring.c +++ b/engines/io_uring.c @@ -80,6 +80,7 @@ struct ioring_options { unsigned int sqpoll_cpu; unsigned int nonvectored; unsigned int uncached; + unsigned int nowait; }; static const int ddir_to_op[2][2] = { @@ -185,6 +186,15 @@ static struct fio_option options[] = { .category = FIO_OPT_C_ENGINE, .group = FIO_OPT_G_IOURING, }, + { + .name = "nowait", + .lname = "RWF_NOWAIT", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct ioring_options, nowait), + .help = "Use RWF_NOWAIT for reads/writes", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_IOURING, + }, { .name = NULL, }, @@ -235,6 +245,8 @@ static int fio_ioring_prep(struct thread_data *td, struct io_u *io_u) } if (!td->o.odirect && o->uncached) sqe->rw_flags = RWF_UNCACHED; + if (o->nowait) + sqe->rw_flags |= RWF_NOWAIT; if (ld->ioprio_class_set) sqe->ioprio = td->o.ioprio_class << 13; if (ld->ioprio_set) diff --git a/engines/libaio.c b/engines/libaio.c index 299798ae..daa576da 100644 --- a/engines/libaio.c +++ b/engines/libaio.c @@ -21,6 +21,11 @@ #define IOCB_FLAG_IOPRIO (1 << 1) #endif +/* Hack for libaio < 0.3.111 */ +#ifndef CONFIG_LIBAIO_RW_FLAGS +#define aio_rw_flags __pad2 +#endif + static int fio_libaio_commit(struct thread_data *td); static int fio_libaio_init(struct thread_data *td); @@ -51,6 +56,7 @@ struct libaio_options { void *pad; unsigned int userspace_reap; unsigned int cmdprio_percentage; + unsigned int nowait; }; static struct fio_option options[] = { @@ -83,6 +89,15 @@ static struct fio_option options[] = { .help = "Your platform does not support I/O priority classes", }, #endif + { + .name = "nowait", + .lname = "RWF_NOWAIT", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct libaio_options, nowait), + .help = "Set RWF_NOWAIT for reads/writes", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_LIBAIO, + }, { .name = NULL, }, @@ -97,15 +112,20 @@ static inline void ring_inc(struct libaio_data *ld, unsigned int *val, *val = (*val + add) % ld->entries; } -static int fio_libaio_prep(struct thread_data fio_unused *td, struct io_u *io_u) +static int fio_libaio_prep(struct thread_data *td, struct io_u *io_u) { + struct libaio_options *o = td->eo; struct fio_file *f = io_u->file; struct iocb *iocb = &io_u->iocb; if (io_u->ddir == DDIR_READ) { io_prep_pread(iocb, f->fd, io_u->xfer_buf, io_u->xfer_buflen, io_u->offset); + if (o->nowait) + iocb->aio_rw_flags |= RWF_NOWAIT; } else if (io_u->ddir == DDIR_WRITE) { io_prep_pwrite(iocb, f->fd, io_u->xfer_buf, io_u->xfer_buflen, io_u->offset); + if (o->nowait) + iocb->aio_rw_flags |= RWF_NOWAIT; } else if (ddir_sync(io_u->ddir)) io_prep_fsync(iocb, f->fd); diff --git a/engines/sync.c b/engines/sync.c index 65fd210c..339ba999 100644 --- a/engines/sync.c +++ b/engines/sync.c @@ -40,6 +40,7 @@ struct psyncv2_options { unsigned int hipri; unsigned int hipri_percentage; unsigned int uncached; + unsigned int nowait; }; static struct fio_option options[] = { @@ -73,6 +74,15 @@ static struct fio_option options[] = { .category = FIO_OPT_C_ENGINE, .group = FIO_OPT_G_INVALID, }, + { + .name = "nowait", + .lname = "RWF_NOWAIT", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct psyncv2_options, nowait), + .help = "Set RWF_NOWAIT for pwritev2/preadv2", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_INVALID, + }, { .name = NULL, }, @@ -164,6 +174,8 @@ static enum fio_q_status fio_pvsyncio2_queue(struct thread_data *td, flags |= RWF_HIPRI; if (!td->o.odirect && o->uncached) flags |= RWF_UNCACHED; + if (o->nowait) + flags |= RWF_NOWAIT; iov->iov_base = io_u->xfer_buf; iov->iov_len = io_u->xfer_buflen; diff --git a/fio.1 b/fio.1 index 47bc1592..f469c46e 100644 --- a/fio.1 +++ b/fio.1 @@ -1857,6 +1857,22 @@ than normal. When hipri is set this determines the probability of a pvsync2 I/O being high priority. The default is 100%. .TP +.BI (pvsync2,libaio,io_uring)nowait +By default if a request cannot be executed immediately (e.g. resource starvation, +waiting on locks) it is queued and the initiating process will be blocked until +the required resource becomes free. +This option sets the RWF_NOWAIT flag (supported from the 4.14 Linux kernel) and +the call will return instantly with EAGAIN or a partial result rather than waiting. + +It is useful to also use \fBignore_error\fR=EAGAIN when using this option. +Note: glibc 2.27, 2.28 have a bug in syscall wrappers preadv2, pwritev2. +They return EOPNOTSUP instead of EAGAIN. + +For cached I/O, using this option usually means a request operates only with +cached data. Currently the RWF_NOWAIT flag does not supported for cached write. +For direct I/O, requests will only succeed if cache invalidation isn't required, +file blocks are fully allocated and the disk request could be issued immediately. +.TP .BI (cpuio)cpuload \fR=\fPint Attempt to use the specified percentage of CPU cycles. This is a mandatory option when using cpuio I/O engine. diff --git a/init.c b/init.c index e220c323..e53be35a 100644 --- a/init.c +++ b/init.c @@ -956,7 +956,6 @@ static int fixup_options(struct thread_data *td) */ o->max_latency *= 1000ULL; o->latency_target *= 1000ULL; - o->latency_window *= 1000ULL; return ret; } diff --git a/json.h b/json.h index 1544ed76..d9824263 100644 --- a/json.h +++ b/json.h @@ -82,7 +82,7 @@ static inline int json_object_add_value_string(struct json_object *obj, .type = JSON_TYPE_STRING, }; - arg.string = (char *)val; + arg.string = strdup(val ? : ""); return json_object_add_value_type(obj, name, &arg); } diff --git a/os/os-linux.h b/os/os-linux.h index 0f0bcc3a..6ec7243d 100644 --- a/os/os-linux.h +++ b/os/os-linux.h @@ -325,6 +325,9 @@ static inline int fio_set_sched_idle(void) #ifndef RWF_SYNC #define RWF_SYNC 0x00000004 #endif +#ifndef RWF_NOWAIT +#define RWF_NOWAIT 0x00000008 +#endif #ifndef RWF_UNCACHED #define RWF_UNCACHED 0x00000040 diff --git a/stat.c b/stat.c index 2cf11947..b3951199 100644 --- a/stat.c +++ b/stat.c @@ -1506,12 +1506,8 @@ static void json_add_job_opts(struct json_object *root, const char *name, json_object_add_value_object(root, name, dir_object); flist_for_each(entry, opt_list) { - const char *pos = ""; - p = flist_entry(entry, struct print_option, list); - if (p->value) - pos = p->value; - json_object_add_value_string(dir_object, p->name, pos); + json_object_add_value_string(dir_object, p->name, p->value); } }