It allows configuring how the engine waits for request completions,
instead of always using a blocking blkioq_do_io() call.
Signed-off-by: Alberto Faria <afaria@xxxxxxxxxx>
---
HOWTO.rst | 14 ++++-
engines/libblkio.c | 132 ++++++++++++++++++++++++++++++++++++++++++---
fio.1 | 16 +++++-
3 files changed, 154 insertions(+), 8 deletions(-)
diff --git a/HOWTO.rst b/HOWTO.rst
index 7155add6..4b059cb2 100644
--- a/HOWTO.rst
+++ b/HOWTO.rst
@@ -2332,7 +2332,8 @@ with the caveat that when used on the command line, they must come after the
[libblkio]
- Use poll queues.
+ Use poll queues. This is incompatible with
+ :option:`libblkio_wait_mode=eventfd <libblkio_wait_mode>`.
[pvsync2]
@@ -2884,6 +2885,17 @@ with the caveat that when used on the command line, they must come after the
Submit trims as "write zeroes" requests instead of discard requests.
Default is 0.
+.. option:: libblkio_wait_mode=str : [libblkio]
+
+ How to wait for completions:
+
+ **block** (default)
+ Use a blocking call to ``blkioq_do_io()``.
+ **eventfd**
+ Use a blocking call to ``read()`` on the completion eventfd.
+ **loop**
+ Use a busy loop with a non-blocking call to ``blkioq_do_io()``.
+
I/O depth
~~~~~~~~~
diff --git a/engines/libblkio.c b/engines/libblkio.c
index 79af3aa7..d7666f69 100644
--- a/engines/libblkio.c
+++ b/engines/libblkio.c
@@ -20,6 +20,12 @@
#include "../options.h"
#include "../parse.h"
+enum fio_blkio_wait_mode {
+ FIO_BLKIO_WAIT_MODE_BLOCK,
+ FIO_BLKIO_WAIT_MODE_EVENTFD,
+ FIO_BLKIO_WAIT_MODE_LOOP,
+};
+
struct fio_blkio_options {
void *pad; /* option fields must not have offset 0 */
@@ -30,6 +36,7 @@ struct fio_blkio_options {
unsigned int hipri;
unsigned int vectored;
unsigned int write_zeroes_on_trim;
+ enum fio_blkio_wait_mode wait_mode;
};
static struct fio_option options[] = {
@@ -89,6 +96,30 @@ static struct fio_option options[] = {
.category = FIO_OPT_C_ENGINE,
.group = FIO_OPT_G_LIBBLKIO,
},
+ {
+ .name = "libblkio_wait_mode",
+ .lname = "How to wait for completions",
+ .type = FIO_OPT_STR,
+ .off1 = offsetof(struct fio_blkio_options, wait_mode),
+ .help = "How to wait for completions",
+ .def = "block",
+ .posval = {
+ { .ival = "block",
+ .oval = FIO_BLKIO_WAIT_MODE_BLOCK,
+ .help = "Blocking blkioq_do_io()",
+ },
+ { .ival = "eventfd",
+ .oval = FIO_BLKIO_WAIT_MODE_EVENTFD,
+ .help = "Blocking read() on the completion eventfd",
+ },
+ { .ival = "loop",
+ .oval = FIO_BLKIO_WAIT_MODE_LOOP,
+ .help = "Busy loop with non-blocking blkioq_do_io()",
+ },
+ },
+ .category = FIO_OPT_C_ENGINE,
+ .group = FIO_OPT_G_LIBBLKIO,
+ },
{
.name = NULL,
@@ -237,12 +268,21 @@ static int fio_blkio_setup(struct thread_data *td)
* again in the init() callback.
*/
+ const struct fio_blkio_options *options = td->eo;
struct blkio *b;
int ret = 0;
uint64_t capacity;
assert(td->files_index == 1);
+ /* validate options */
+
+ if (options->hipri &&
+ options->wait_mode == FIO_BLKIO_WAIT_MODE_EVENTFD) {
+ log_err("fio: option hipri is incompatible with option libblkio_wait_mode=eventfd\n");
+ return 1;
+ }
+
/* get target size */
if (fio_blkio_create_and_connect(td, &b) != 0)
@@ -268,6 +308,7 @@ out_blkio_destroy:
struct fio_blkio_data {
struct blkio *b;
struct blkioq *q;
+ int completion_fd; /* -1 if not FIO_BLKIO_WAIT_MODE_EVENTFD */
bool has_mem_region; /* whether mem_region is valid */
struct blkio_mem_region mem_region; /* only if allocated by libblkio */
@@ -280,6 +321,7 @@ static int fio_blkio_init(struct thread_data *td)
{
const struct fio_blkio_options *options = td->eo;
struct fio_blkio_data *data;
+ int flags;
/* allocate per-thread data struct */
@@ -324,6 +366,31 @@ static int fio_blkio_init(struct thread_data *td)
else
data->q = blkio_get_queue(data->b, 0);
+ /* enable completion fd and make it blocking */
+
+ if (options->wait_mode == FIO_BLKIO_WAIT_MODE_EVENTFD) {
+ blkioq_set_completion_fd_enabled(data->q, true);
+
+ data->completion_fd = blkioq_get_completion_fd(data->q);
+
+ flags = fcntl(data->completion_fd, F_GETFL);
+ if (flags < 0) {
+ log_err("fio: fcntl(F_GETFL) failed: %s\n",
+ strerror(errno));
+ goto err_blkio_destroy;
+ }
+
+ flags &= ~O_NONBLOCK;
+
+ if (fcntl(data->completion_fd, F_SETFL, flags) != 0) {
+ log_err("fio: fcntl(F_SETFL) failed: %s\n",
+ strerror(errno));
+ goto err_blkio_destroy;
+ }
+ } else {
+ data->completion_fd = -1;
+ }
+
/* Set data only here so cleanup() does nothing if init() fails. */
td->io_ops_data = data;
@@ -521,16 +588,69 @@ static enum fio_q_status fio_blkio_queue(struct thread_data *td,
static int fio_blkio_getevents(struct thread_data *td, unsigned int min,
unsigned int max, const struct timespec *t)
{
+ const struct fio_blkio_options *options = td->eo;
struct fio_blkio_data *data = td->io_ops_data;
- int n;
+ int ret, n;
+ uint64_t event;
- n = blkioq_do_io(data->q, data->completions, (int)min, (int)max, NULL);
- if (n < 0) {
- fio_blkio_log_err(blkioq_do_io);
+ switch (options->wait_mode) {
+ case FIO_BLKIO_WAIT_MODE_BLOCK:
+
+ n = blkioq_do_io(data->q, data->completions, (int)min, (int)max,
+ NULL);
+ if (n < 0) {
+ fio_blkio_log_err(blkioq_do_io);
+ return -1;
+ }
+
+ return n;
+
+ case FIO_BLKIO_WAIT_MODE_EVENTFD:
+
+ n = blkioq_do_io(data->q, data->completions, 0, (int)max, NULL);
+ if (n < 0) {
+ fio_blkio_log_err(blkioq_do_io);
+ return -1;
+ }
+
+ while (n < (int)min) {
+ ret = read(data->completion_fd, &event, sizeof(event));
+ if (ret != sizeof(event)) {
+ log_err("fio: read() on the completion fd returned %d\n",
+ ret);
+ return -1;
+ }
+
+ ret = blkioq_do_io(data->q, data->completions + n, 0,
+ (int)max - n, NULL);
+ if (ret < 0) {
+ fio_blkio_log_err(blkioq_do_io);
+ return -1;
+ }
+
+ n += ret;
+ }
+
+ return n;
+
+ case FIO_BLKIO_WAIT_MODE_LOOP:
+
+ for (n = 0; n < (int)min; ) {
+ ret = blkioq_do_io(data->q, data->completions + n, 0,
+ (int)max - n, NULL);
+ if (ret < 0) {
+ fio_blkio_log_err(blkioq_do_io);
+ return -1;
+ }
+
+ n += ret;
+ }
+
+ return n;
+
+ default:
return -1;
}
-
- return n;
}
static struct io_u *fio_blkio_event(struct thread_data *td, int event)
diff --git a/fio.1 b/fio.1
index 053c2eda..88bb232f 100644
--- a/fio.1
+++ b/fio.1
@@ -2622,7 +2622,7 @@ before starting the \fBstruct blkio\fR. Each property must have the format
the engine sets any other properties, so those can be overriden.
.TP
.BI (libblkio)hipri \fR=\fPbool
-Use poll queues.
+Use poll queues. This is incompatible with \fBlibblkio_wait_mode=eventfd\fR.
.TP
.BI (libblkio)libblkio_vectored \fR=\fPbool
Submit vectored read and write requests. Default is 0.
@@ -2630,6 +2630,20 @@ Submit vectored read and write requests. Default is 0.
.BI (libblkio)libblkio_write_zeroes_on_trim \fR=\fPbool
Submit trims as "write zeroes" requests instead of discard requests. Default is
0.
+.TP
+.BI (libblkio)libblkio_wait_mode \fR=\fPstr
+How to wait for completions:
+.RS
+.RS
+.TP
+.B block \fR(default)
+Use a blocking call to \fBblkioq_do_io()\fR.
+.TP
+.B eventfd
+Use a blocking call to \fBread()\fR on the completion eventfd.
+.TP
+.B loop
+Use a busy loop with a non-blocking call to \fBblkioq_do_io()\fR.
.SS "I/O depth"
.TP
.BI iodepth \fR=\fPint