Re: [PATCH 07/10] engines/libblkio: Add option libblkio_wait_mode

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 11/21/22 13:28, Alberto Faria wrote:
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:
+

Because we already have indentation here the blank lines before and after each "case" don't improve readability much. To be consistent with the rest of the code base and conserve vertical screen real estate I would drop the blank lines.

+		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




[Index of Archives]     [Linux Kernel]     [Linux SCSI]     [Linux IDE]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux