libaio,io_uring: improve cmdprio_percentage option
authorDamien Le Moal <damien.lemoal@wdc.com>
Fri, 3 Sep 2021 15:20:24 +0000 (15:20 +0000)
committerJens Axboe <axboe@kernel.dk>
Fri, 3 Sep 2021 16:12:17 +0000 (10:12 -0600)
The cmdprio_percentage option of the libaio and io_uring engines defines
a single percentage that applies to all IO operations, regardless of
their direction. This prevents defining different high priority IO
percentages for reads and writes operations. This differentiation can
however be useful in the case of a mixed read-write workload (rwmixread
and rwmixwrite options).

Change the option definition to allow specifying a comma separated list
of percentages, 2 at most, one for reads and one for writes. If only a
single percentage is defined, it applies to both reads and writes as
before. The cmdprio_percentage option becomes an array of DDIR_RWDIR_CNT
elements indexed with enum fio_ddir values. The last entry of the array
(for DDIR_TRIM) is always 0.

Also create a new cmdprio helper file, engines/cmdprio.h,
such that we can avoid code duplication between io_uring and libaio
io engines. This helper file will be extended in subsequent patches.

Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
HOWTO
engines/cmdprio.h [new file with mode: 0644]
engines/io_uring.c
engines/libaio.c
fio.1

diff --git a/HOWTO b/HOWTO
index 96b680dd9535c2ad89dcf35b225caf9dccd5b936..916f5191f0327be2c21ada2cc776228f0dfd3ad8 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -2163,14 +2163,14 @@ In addition, there are some parameters which are only valid when a specific
 with the caveat that when used on the command line, they must come after the
 :option:`ioengine` that defines them is selected.
 
-.. option:: cmdprio_percentage=int : [io_uring] [libaio]
-
-    Set the percentage of I/O that will be issued with higher priority by setting
-    the priority bit. Non-read I/O is likely unaffected by ``cmdprio_percentage``.
-    This option cannot be used with the `prio` or `prioclass` options. For this
-    option to set the priority bit properly, NCQ priority must be supported and
-    enabled and :option:`direct`\=1 option must be used. fio must also be run as
-    the root user.
+.. option:: cmdprio_percentage=int[,int] : [io_uring] [libaio]
+
+    Set the percentage of I/O that will be issued with the highest priority.
+    Default: 0. A single value applies to reads and writes. Comma-separated
+    values may be specified for reads and writes. This option cannot be used
+    with the :option:`prio` or :option:`prioclass` options. For this option
+    to be effective, NCQ priority must be supported and enabled, and `direct=1'
+    option must be used. fio must also be run as the root user.
 
 .. option:: fixedbufs : [io_uring]
 
diff --git a/engines/cmdprio.h b/engines/cmdprio.h
new file mode 100644 (file)
index 0000000..19120d7
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * IO priority handling declarations and helper functions common to the
+ * libaio and io_uring engines.
+ */
+
+#ifndef FIO_CMDPRIO_H
+#define FIO_CMDPRIO_H
+
+#include "../fio.h"
+
+struct cmdprio {
+       unsigned int percentage[DDIR_RWDIR_CNT];
+};
+
+static int fio_cmdprio_init(struct thread_data *td, struct cmdprio *cmdprio,
+                           bool *has_cmdprio)
+{
+       struct thread_options *to = &td->o;
+       bool has_cmdprio_percentage = false;
+       int i;
+
+       for (i = 0; i < DDIR_RWDIR_CNT; i++) {
+               if (cmdprio->percentage[i])
+                       has_cmdprio_percentage = true;
+       }
+
+       /*
+        * Check for option conflicts
+        */
+       if (has_cmdprio_percentage &&
+           (fio_option_is_set(to, ioprio) ||
+            fio_option_is_set(to, ioprio_class))) {
+               log_err("%s: cmdprio_percentage option and mutually exclusive "
+                       "prio or prioclass option is set, exiting\n",
+                       to->name);
+               return 1;
+       }
+
+       *has_cmdprio = has_cmdprio_percentage;
+
+       return 0;
+}
+
+#endif
index 4f8b558281d46ff78ad51f2f20d30af8b577c82a..1731eb2463af078e6c4d66828d71ba7a1c3c5cca 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "../lib/types.h"
 #include "../os/linux/io_uring.h"
+#include "cmdprio.h"
 
 struct io_sq_ring {
        unsigned *head;
@@ -69,12 +70,14 @@ struct ioring_data {
        int prepped;
 
        struct ioring_mmap mmap[3];
+
+       bool use_cmdprio;
 };
 
 struct ioring_options {
        void *pad;
        unsigned int hipri;
-       unsigned int cmdprio_percentage;
+       struct cmdprio cmdprio;
        unsigned int fixedbufs;
        unsigned int registerfiles;
        unsigned int sqpoll_thread;
@@ -120,8 +123,11 @@ static struct fio_option options[] = {
                .name   = "cmdprio_percentage",
                .lname  = "high priority percentage",
                .type   = FIO_OPT_INT,
-               .off1   = offsetof(struct ioring_options, cmdprio_percentage),
-               .minval = 1,
+               .off1   = offsetof(struct ioring_options,
+                                  cmdprio.percentage[DDIR_READ]),
+               .off2   = offsetof(struct ioring_options,
+                                  cmdprio.percentage[DDIR_WRITE]),
+               .minval = 0,
                .maxval = 100,
                .help   = "Send high priority I/O this percentage of the time",
                .category = FIO_OPT_C_ENGINE,
@@ -381,13 +387,16 @@ static void fio_ioring_prio_prep(struct thread_data *td, struct io_u *io_u)
 {
        struct ioring_options *o = td->eo;
        struct ioring_data *ld = td->io_ops_data;
-       if (rand_between(&td->prio_state, 0, 99) < o->cmdprio_percentage) {
-               ld->sqes[io_u->index].ioprio = ioprio_value(IOPRIO_CLASS_RT, 0);
+       struct io_uring_sqe *sqe = &ld->sqes[io_u->index];
+       struct cmdprio *cmdprio = &o->cmdprio;
+       unsigned int p = cmdprio->percentage[io_u->ddir];
+
+       if (p && rand_between(&td->prio_state, 0, 99) < p) {
+               sqe->ioprio = ioprio_value(IOPRIO_CLASS_RT, 0);
                io_u->flags |= IO_U_F_PRIORITY;
        } else {
-               ld->sqes[io_u->index].ioprio = 0;
+               sqe->ioprio = 0;
        }
-       return;
 }
 
 static enum fio_q_status fio_ioring_queue(struct thread_data *td,
@@ -395,7 +404,6 @@ static enum fio_q_status fio_ioring_queue(struct thread_data *td,
 {
        struct ioring_data *ld = td->io_ops_data;
        struct io_sq_ring *ring = &ld->sq_ring;
-       struct ioring_options *o = td->eo;
        unsigned tail, next_tail;
 
        fio_ro_check(td, io_u);
@@ -418,7 +426,7 @@ static enum fio_q_status fio_ioring_queue(struct thread_data *td,
        if (next_tail == atomic_load_acquire(ring->head))
                return FIO_Q_BUSY;
 
-       if (o->cmdprio_percentage)
+       if (ld->use_cmdprio)
                fio_ioring_prio_prep(td, io_u);
        ring->array[tail & ld->sq_ring_mask] = io_u->index;
        atomic_store_release(ring->tail, next_tail);
@@ -729,7 +737,8 @@ static int fio_ioring_init(struct thread_data *td)
 {
        struct ioring_options *o = td->eo;
        struct ioring_data *ld;
-       struct thread_options *to = &td->o;
+       struct cmdprio *cmdprio = &o->cmdprio;
+       int ret;
 
        /* sqthread submission requires registered files */
        if (o->sqpoll_thread)
@@ -753,14 +762,9 @@ static int fio_ioring_init(struct thread_data *td)
 
        td->io_ops_data = ld;
 
-       /*
-        * Check for option conflicts
-        */
-       if ((fio_option_is_set(to, ioprio) || fio_option_is_set(to, ioprio_class)) &&
-                       o->cmdprio_percentage != 0) {
-               log_err("%s: cmdprio_percentage option and mutually exclusive "
-                               "prio or prioclass option is set, exiting\n", to->name);
-               td_verror(td, EINVAL, "fio_io_uring_init");
+       ret = fio_cmdprio_init(td, cmdprio, &ld->use_cmdprio);
+       if (ret) {
+               td_verror(td, EINVAL, "fio_ioring_init");
                return 1;
        }
 
index b12b6ffcefbbf364dd227a9d86592f26130156ca..8cf560c52977390fdca0034a9bdfc9dff39f84f6 100644 (file)
@@ -15,6 +15,7 @@
 #include "../lib/pow2.h"
 #include "../optgroup.h"
 #include "../lib/memalign.h"
+#include "cmdprio.h"
 
 /* Should be defined in newest aio_abi.h */
 #ifndef IOCB_FLAG_IOPRIO
@@ -50,12 +51,14 @@ struct libaio_data {
        unsigned int queued;
        unsigned int head;
        unsigned int tail;
+
+       bool use_cmdprio;
 };
 
 struct libaio_options {
        void *pad;
        unsigned int userspace_reap;
-       unsigned int cmdprio_percentage;
+       struct cmdprio cmdprio;
        unsigned int nowait;
 };
 
@@ -74,8 +77,11 @@ static struct fio_option options[] = {
                .name   = "cmdprio_percentage",
                .lname  = "high priority percentage",
                .type   = FIO_OPT_INT,
-               .off1   = offsetof(struct libaio_options, cmdprio_percentage),
-               .minval = 1,
+               .off1   = offsetof(struct libaio_options,
+                                  cmdprio.percentage[DDIR_READ]),
+               .off2   = offsetof(struct libaio_options,
+                                  cmdprio.percentage[DDIR_WRITE]),
+               .minval = 0,
                .maxval = 100,
                .help   = "Send high priority I/O this percentage of the time",
                .category = FIO_OPT_C_ENGINE,
@@ -135,12 +141,14 @@ static int fio_libaio_prep(struct thread_data *td, struct io_u *io_u)
 static void fio_libaio_prio_prep(struct thread_data *td, struct io_u *io_u)
 {
        struct libaio_options *o = td->eo;
-       if (rand_between(&td->prio_state, 0, 99) < o->cmdprio_percentage) {
+       struct cmdprio *cmdprio = &o->cmdprio;
+       unsigned int p = cmdprio->percentage[io_u->ddir];
+
+       if (p && rand_between(&td->prio_state, 0, 99) < p) {
                io_u->iocb.aio_reqprio = ioprio_value(IOPRIO_CLASS_RT, 0);
                io_u->iocb.u.c.flags |= IOCB_FLAG_IOPRIO;
                io_u->flags |= IO_U_F_PRIORITY;
        }
-       return;
 }
 
 static struct io_u *fio_libaio_event(struct thread_data *td, int event)
@@ -246,7 +254,6 @@ static enum fio_q_status fio_libaio_queue(struct thread_data *td,
                                          struct io_u *io_u)
 {
        struct libaio_data *ld = td->io_ops_data;
-       struct libaio_options *o = td->eo;
 
        fio_ro_check(td, io_u);
 
@@ -277,7 +284,7 @@ static enum fio_q_status fio_libaio_queue(struct thread_data *td,
                return FIO_Q_COMPLETED;
        }
 
-       if (o->cmdprio_percentage)
+       if (ld->use_cmdprio)
                fio_libaio_prio_prep(td, io_u);
 
        ld->iocbs[ld->head] = &io_u->iocb;
@@ -420,8 +427,9 @@ static int fio_libaio_post_init(struct thread_data *td)
 static int fio_libaio_init(struct thread_data *td)
 {
        struct libaio_data *ld;
-       struct thread_options *to = &td->o;
        struct libaio_options *o = td->eo;
+       struct cmdprio *cmdprio = &o->cmdprio;
+       int ret;
 
        ld = calloc(1, sizeof(*ld));
 
@@ -432,16 +440,13 @@ static int fio_libaio_init(struct thread_data *td)
        ld->io_us = calloc(ld->entries, sizeof(struct io_u *));
 
        td->io_ops_data = ld;
-       /*
-        * Check for option conflicts
-        */
-       if ((fio_option_is_set(to, ioprio) || fio_option_is_set(to, ioprio_class)) &&
-                       o->cmdprio_percentage != 0) {
-               log_err("%s: cmdprio_percentage option and mutually exclusive "
-                               "prio or prioclass option is set, exiting\n", to->name);
+
+       ret = fio_cmdprio_init(td, cmdprio, &ld->use_cmdprio);
+       if (ret) {
                td_verror(td, EINVAL, "fio_libaio_init");
                return 1;
        }
+
        return 0;
 }
 
diff --git a/fio.1 b/fio.1
index 87ca8e73d37f6f19c7c8dc46242080cb07a10c55..3611da98a9fcaab5f3daa2f7389395ed19c018b8 100644 (file)
--- a/fio.1
+++ b/fio.1
@@ -1962,13 +1962,13 @@ In addition, there are some parameters which are only valid when a specific
 with the caveat that when used on the command line, they must come after the
 \fBioengine\fR that defines them is selected.
 .TP
-.BI (io_uring,libaio)cmdprio_percentage \fR=\fPint
-Set the percentage of I/O that will be issued with higher priority by setting
-the priority bit. Non-read I/O is likely unaffected by ``cmdprio_percentage``.
-This option cannot be used with the `prio` or `prioclass` options. For this
-option to set the priority bit properly, NCQ priority must be supported and
-enabled and `direct=1' option must be used. fio must also be run as the root
-user.
+.BI (io_uring,libaio)cmdprio_percentage \fR=\fPint[,int]
+Set the percentage of I/O that will be issued with the highest priority.
+Default: 0. A single value applies to reads and writes. Comma-separated
+values may be specified for reads and writes. This option cannot be used
+with the `prio` or `prioclass` options. For this option to be effective,
+NCQ priority must be supported and enabled, and `direct=1' option must be
+used. fio must also be run as the root user.
 .TP
 .BI (io_uring)fixedbufs
 If fio is asked to do direct IO, then Linux will map pages for each IO call, and