Add thinktime_blocks_type parameter
authorHongwei Qin <glqinhongwei@gmail.com>
Mon, 25 Jan 2021 12:39:59 +0000 (20:39 +0800)
committerJens Axboe <axboe@kernel.dk>
Tue, 26 Jan 2021 15:58:34 +0000 (08:58 -0700)
This patch adds a new parameter thinktime_blocks_type to control the
behavior of thinktime_blocks. It can be either `complete` or `issue`. If
it is `complete` (default), fio triggers thinktime when thinktime_blocks
number of blocks are **completed**. If it is `issue`, fio triggers
thinktime when thinktime_blocks number of blocks are **issued**

Tests:
jobfile1:
```
[global]
thread
kb_base=1000
direct=1
size=1GiB
group_reporting
io_size=96KiB
ioengine=libaio
iodepth=8
bs=4096
filename=/dev/qblkdev
rw=randwrite

[fio_randwrite]
thinktime=2s
thinktime_blocks=4
```

jobfile2:
```
[global]
thread
kb_base=1000
direct=1
size=1GiB
group_reporting
io_size=96KiB
ioengine=libaio
iodepth=8
bs=4096
filename=/dev/qblkdev
rw=randwrite

[fio_randwrite]
thinktime=2s
thinktime_blocks=4
thinktime_blocks_type=issue
```

Results:
Current HEAD:
fio jobfile1:
write: IOPS=5, BW=24.6kB/s (24.0KiB/s)(98.3kB/4002msec); 0 zone resets
blktrace:
11 reqs -- 2s -- 8 reqs -- 2s -- 5 reqs -- end

This patch:
fio jobfile1:
write: IOPS=5, BW=24.6kB/s (24.0KiB/s)(98.3kB/4001msec); 0 zone resets
blktrace:
11 reqs -- 2s -- 8 reqs -- 2s -- 5 reqs -- end

fio jobfile2:
write: IOPS=1, BW=8190B/s (8190B/s)(98.3kB/12002msec); 0 zone resets
blktrace:
4 reqs -- 2s -- 4 reqs ... -- 4 reqs -- 2s -- end

Server:
fio --server=192.168.1.172,8765
Client (On the same machine):
fio --client=192.168.1.172,8765 jobfile1
write: IOPS=5, BW=24.6kB/s (24.0KiB/s)(98.3kB/4001msec); 0 zone resets
blktrace:
11 reqs -- 2s -- 8 reqs -- 2s -- 5 reqs -- end

fio --client=192.168.1.172,8765 jobfile2
write: IOPS=1, BW=8191B/s (8191B/s)(98.3kB/12001msec); 0 zone resets
blktrace:
4 reqs -- 2s -- 4 reqs ... -- 4 reqs -- 2s -- end

Signed-off-by: Hongwei Qin <glqinhongwei@gmail.com>
[axboe: fold patch 3 into this one]
Signed-off-by: Jens Axboe <axboe@kernel.dk>
HOWTO
backend.c
cconv.c
engines/cpu.c
fio.1
fio.h
options.c
thread_options.h

diff --git a/HOWTO b/HOWTO
index 3ec86aff134732ef941d78f6ba69ae2154a29f38..b6d1b58a3af46147091fb1a3828362fff10b0e0a 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -2574,6 +2574,13 @@ I/O rate
        before we have to complete it and do our :option:`thinktime`. In other words, this
        setting effectively caps the queue depth if the latter is larger.
 
+.. option:: thinktime_blocks_type=str
+
+       Only valid if :option:`thinktime` is set - control how :option:`thinktime_blocks`
+       triggers. The default is `complete`, which triggers thinktime when fio completes
+       :option:`thinktime_blocks` blocks. If this is set to `issue`, then the trigger happens
+       at the issue side.
+
 .. option:: rate=int[,int][,int]
 
        Cap the bandwidth used by this job. The number is in bytes/sec, the normal
index e20a2e07766eabf4ea0f86dfde6db40e9ed621f7..874e19379067944f4b3930f4ad31056e41583916 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -864,8 +864,8 @@ static void handle_thinktime(struct thread_data *td, enum fio_ddir ddir)
        uint64_t total;
        int left;
 
-       b = ddir_rw_sum(td->io_blocks);
-       if (b % td->o.thinktime_blocks)
+       b = ddir_rw_sum(td->thinktime_blocks_counter);
+       if (b % td->o.thinktime_blocks || !b)
                return;
 
        io_u_quiesce(td);
@@ -1076,6 +1076,10 @@ reap:
                }
                if (ret < 0)
                        break;
+
+               if (ddir_rw(ddir) && td->o.thinktime)
+                       handle_thinktime(td, ddir);
+
                if (!ddir_rw_sum(td->bytes_done) &&
                    !td_ioengine_flagged(td, FIO_NOIO))
                        continue;
@@ -1090,9 +1094,6 @@ reap:
                }
                if (!in_ramp_time(td) && td->o.latency_target)
                        lat_target_check(td);
-
-               if (ddir_rw(ddir) && td->o.thinktime)
-                       handle_thinktime(td, ddir);
        }
 
        check_update_rusage(td);
@@ -1744,6 +1745,11 @@ static void *thread_main(void *data)
        if (rate_submit_init(td, sk_out))
                goto err;
 
+       if (td->o.thinktime_blocks_type == THINKTIME_BLOCKS_TYPE_COMPLETE)
+               td->thinktime_blocks_counter = td->io_blocks;
+       else
+               td->thinktime_blocks_counter = td->io_issues;
+
        set_epoch_time(td, o->log_unix_epoch);
        fio_getrusage(&td->ru_start);
        memcpy(&td->bw_sample_time, &td->epoch, sizeof(td->epoch));
diff --git a/cconv.c b/cconv.c
index 62c2fc292cd845c82d7b74aaaf752e1174576464..b10868fb3de6b2dd0844799ab36fd145ff68448e 100644 (file)
--- a/cconv.c
+++ b/cconv.c
@@ -210,6 +210,7 @@ void convert_thread_options_to_cpu(struct thread_options *o,
        o->thinktime = le32_to_cpu(top->thinktime);
        o->thinktime_spin = le32_to_cpu(top->thinktime_spin);
        o->thinktime_blocks = le32_to_cpu(top->thinktime_blocks);
+       o->thinktime_blocks_type = le32_to_cpu(top->thinktime_blocks_type);
        o->fsync_blocks = le32_to_cpu(top->fsync_blocks);
        o->fdatasync_blocks = le32_to_cpu(top->fdatasync_blocks);
        o->barrier_blocks = le32_to_cpu(top->barrier_blocks);
@@ -431,6 +432,7 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
        top->thinktime = cpu_to_le32(o->thinktime);
        top->thinktime_spin = cpu_to_le32(o->thinktime_spin);
        top->thinktime_blocks = cpu_to_le32(o->thinktime_blocks);
+       top->thinktime_blocks_type = __cpu_to_le32(o->thinktime_blocks_type);
        top->fsync_blocks = cpu_to_le32(o->fsync_blocks);
        top->fdatasync_blocks = cpu_to_le32(o->fdatasync_blocks);
        top->barrier_blocks = cpu_to_le32(o->barrier_blocks);
index ccbfe00355a0b79d10885d86ded205dbe728935e..ce74dbcea8758e776b03b02d41ebb1489cc490c7 100644 (file)
@@ -268,6 +268,7 @@ static int fio_cpuio_init(struct thread_data *td)
         * set thinktime_sleep and thinktime_spin appropriately
         */
        o->thinktime_blocks = 1;
+       o->thinktime_blocks_type = THINKTIME_BLOCKS_TYPE_COMPLETE;
        o->thinktime_spin = 0;
        o->thinktime = ((unsigned long long) co->cpucycle *
                                (100 - co->cpuload)) / co->cpuload;
diff --git a/fio.1 b/fio.1
index 9636a85f51cb200cd84cc80e876cc7583bdf326c..aa248a3b6da62172a2ee0a27e5b345034ebfe7fd 100644 (file)
--- a/fio.1
+++ b/fio.1
@@ -2323,6 +2323,12 @@ queue depth setting redundant, since no more than 1 I/O will be queued
 before we have to complete it and do our \fBthinktime\fR. In other words, this
 setting effectively caps the queue depth if the latter is larger.
 .TP
+.BI thinktime_blocks_type \fR=\fPstr
+Only valid if \fBthinktime\fR is set - control how \fBthinktime_blocks\fR triggers.
+The default is `complete', which triggers \fBthinktime\fR when fio completes
+\fBthinktime_blocks\fR blocks. If this is set to `issue', then the trigger happens
+at the issue side.
+.TP
 .BI rate \fR=\fPint[,int][,int]
 Cap the bandwidth used by this job. The number is in bytes/sec, the normal
 suffix rules apply. Comma-separated values may be specified for reads,
diff --git a/fio.h b/fio.h
index 062abfa74c54ba7b122fac300348fc390243ac4e..b05cb3dfc395346fe02775e65d7a4fba5288ed43 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -149,6 +149,9 @@ enum {
 
        RATE_PROCESS_LINEAR = 0,
        RATE_PROCESS_POISSON = 1,
+
+       THINKTIME_BLOCKS_TYPE_COMPLETE = 0,
+       THINKTIME_BLOCKS_TYPE_ISSUE = 1,
 };
 
 enum {
@@ -354,6 +357,8 @@ struct thread_data {
        struct fio_sem *sem;
        uint64_t bytes_done[DDIR_RWDIR_CNT];
 
+       uint64_t *thinktime_blocks_counter;
+
        /*
         * State for random io, a bitmap of blocks done vs not done
         */
index 955bf959e49c3ff2a8c1c9912786f45b833b346c..e62e0cfb35413a3f59b8c8d3d141a40d6c44f8b7 100644 (file)
--- a/options.c
+++ b/options.c
@@ -3608,6 +3608,28 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_THINKTIME,
        },
+       {
+               .name   = "thinktime_blocks_type",
+               .lname  = "Thinktime blocks type",
+               .type   = FIO_OPT_STR,
+               .off1   = offsetof(struct thread_options, thinktime_blocks_type),
+               .help   = "How thinktime_blocks takes effect",
+               .def    = "complete",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_THINKTIME,
+               .posval = {
+                         { .ival = "complete",
+                           .oval = THINKTIME_BLOCKS_TYPE_COMPLETE,
+                           .help = "thinktime_blocks takes effect at the completion side",
+                         },
+                         {
+                           .ival = "issue",
+                           .oval = THINKTIME_BLOCKS_TYPE_ISSUE,
+                           .help = "thinktime_blocks takes effect at the issue side",
+                         },
+               },
+               .parent = "thinktime",
+       },
        {
                .name   = "rate",
                .lname  = "I/O rate",
index 0a033430103d075a6e9fda5e5fef43b84fff1c64..f6b15403c4c21df1c183b47337e71a6018e1fe62 100644 (file)
@@ -177,6 +177,7 @@ struct thread_options {
        unsigned int thinktime;
        unsigned int thinktime_spin;
        unsigned int thinktime_blocks;
+       unsigned int thinktime_blocks_type;
        unsigned int fsync_blocks;
        unsigned int fdatasync_blocks;
        unsigned int barrier_blocks;
@@ -479,6 +480,7 @@ struct thread_options_pack {
        uint32_t thinktime;
        uint32_t thinktime_spin;
        uint32_t thinktime_blocks;
+       uint32_t thinktime_blocks_type;
        uint32_t fsync_blocks;
        uint32_t fdatasync_blocks;
        uint32_t barrier_blocks;
@@ -506,6 +508,9 @@ struct thread_options_pack {
        uint32_t stonewall;
        uint32_t new_group;
        uint32_t numjobs;
+
+       uint8_t pad3[4];
+
        /*
         * We currently can't convert these, so don't enable them
         */