[PATCH] backend: correctly handle rate_iops combined with bssplit
Dmitry Fomichev <[email protected]> Wed, 11 Mar 2026 05:58:04 +0900
| Newsgroups | org.kernel.vger.fio |
|---|---|
| Message-ID | <[email protected]> |
Currently, rate_iops does not produce the expected I/O rate with workloads that use 'bssplit' option. Consider the following example configuration - [global] direct=1 time_based runtime=30s ioengine=io_uring thread=1 [bssplit_rate_iops_repro] filename=/dev/sdX rw=randread iodepth=8 bs=64K rate_iops=50 This works correctly and ~50 IOPS I/O rate is logged during the run. If we replace 'bs=64K' with the following bssplit option - bssplit=32ki/20:64ki/40:256ki/10:512ki/25:1mi/5 in the configuration above, then some incorrect (much lower) IOPS values are observed to be in effect at run time. This problem happens because fio, in order to derive the required I/O rate from 'rate_iops' value provided by the user, simply multiplies the IOPS value by the minimum block size (min_bs). Once bps I/O rate is calculated this way, the processing for 'rate' and 'rate_iops' becomes identical. This works if the I/O issued has the uniform min_bs, as in case of using 'bs=64K'. However, with 'bssplit' option in effect, fio may issue I/O with sizes that are much different from min_bs. Yet the code in usec_for_io() currently always calculates I/O issue delays based on min_bs leading to incorrect IOPS being produced. Fix this by modifying usec_for_io() function to check for bssplit+rate_iops being in effect. For this case, derive the IOPS rate from bps 'rate' member of thread data and then calculate the delay to the next I/O using the IOPS value, not the bps rate. Signed-off-by: Dmitry Fomichev <[email protected]> --- backend.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/backend.c b/backend.c index 6dd078c4..2a171bfa 100644 --- a/backend.c +++ b/backend.c @@ -842,15 +842,31 @@ static long long usec_for_io(struct thread_data *td, enum fio_ddir ddir) } td->last_usec[ddir] += val; return td->last_usec[ddir]; - } else if (bps) { + } + + if (!bps) + return 0; + + /* + * For rate_iops option combined with bssplit, recover + * the user provided IOPS value and calculate the I/O delay + * based on this value, not on bps. + */ + if (!td->o.rate[ddir] && td->o.bssplit_nr[ddir]) { + uint64_t iops = bps / td->o.min_bs[ddir]; + + if (!iops) + return 0; + + td->last_usec[ddir] += (int64_t)(1000000 / iops); + return td->last_usec[ddir]; + } else { uint64_t bytes = td->rate_io_issue_bytes[ddir]; uint64_t secs = bytes / bps; uint64_t remainder = bytes % bps; return remainder * 1000000 / bps + secs * 1000000; } - - return 0; } static void init_thinktime(struct thread_data *td) -- 2.37.3