[PATCH 1/2] verify: fix write tracking in error cases

Shin'ichiro Kawasaki <[email protected]> Sat, 9 Aug 2025 10:15:54 +0900
Newsgroups org.kernel.vger.fio
Message-ID <[email protected]>
Commit a9ba7cef70a7 ("verify: rework write tracking for use with
verify_save_state()") reworked write tracking for verify workloads.
As part of the rework, two new functions were added: log_inflight() and
invalidate_inflight(). log_inflight() is called when a write io_u is
prepared for the verify workload. The call is ensured to occur only once
per write io_u by using the IO_U_F_PATTERN_DONE flag.
invalidate_inflight() is called when the write io_u completes. Under
non-failure conditions, the number of calls to these two functions
is expected to be equal upon the completions of all writes.

However, when a write io_u fails with an error, the balance between the
calls to log_inflight() and invalidate_inflight() breaks for two
reasons. Firstly, invalidate_inflight() is not called when the failed
sync writes. Secondly, the IO_U_F_PATTERN_DONE flag is not cleared when
the failed write io_u is reused. As a result the subsequent attempt to
prepare the io_u does not trigger the call to log_inflight(). The
unbalanced function calls causes mismatch between io_u->numberio and
td->inflight_issued, then results in abort in log_inflight(). This
failure symptom is observed by running t/zbd/run-tests-against-nullb for
the test case 72 in t/zbd/test-zbd-support.

To fix the unbalanced function calls, add calls to log_inflight() in
the sync write failure path in io_queue_event(). This ensures that
failed sync writes are appropriately logged. To fix the left
IO_U_F_PATTERN_DONE flag, clear the flag in io_queue_event() failure
path. Additional, clear the IO_U_F_BUSY_OK flag which is to be cleared
when inflight I/Os complete. For the code simplicity, introduce a new
helper function io_u_clear_inflight_flags() to avoid duplication of
the code to clear the flags.

Fixes: a9ba7cef70a7 ("verify: rework write tracking for use with verify_save_state()")
Signed-off-by: Shin'ichiro Kawasaki <[email protected]>
---
 backend.c |  2 ++
 io_u.c    | 11 +++++++++--
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/backend.c b/backend.c
index 922309a0..858506c4 100644
--- a/backend.c
+++ b/backend.c
@@ -449,6 +449,7 @@ int io_queue_event(struct thread_data *td, struct io_u *io_u, int *ret,
 	case FIO_Q_COMPLETED:
 		if (io_u->error) {
 			*ret = -io_u->error;
+			invalidate_inflight(td, io_u);
 			clear_io_u(td, io_u);
 		} else if (io_u->resid) {
 			long long bytes = io_u->xfer_buflen - io_u->resid;
@@ -467,6 +468,7 @@ int io_queue_event(struct thread_data *td, struct io_u *io_u, int *ret,
 				if (!from_verify)
 					unlog_io_piece(td, io_u);
 				td_verror(td, EIO, "full resid");
+				invalidate_inflight(td, io_u);
 				clear_io_u(td, io_u);
 				break;
 			}
diff --git a/io_u.c b/io_u.c
index 6d0f32a8..78dbac9c 100644
--- a/io_u.c
+++ b/io_u.c
@@ -869,9 +869,16 @@ void put_io_u(struct thread_data *td, struct io_u *io_u)
 		__td_io_u_unlock(td);
 }
 
+static inline void io_u_clear_inflight_flags(struct thread_data *td,
+					      struct io_u *io_u)
+{
+	io_u_clear(td, io_u, IO_U_F_FLIGHT | IO_U_F_BUSY_OK |
+		   IO_U_F_PATTERN_DONE);
+}
+
 void clear_io_u(struct thread_data *td, struct io_u *io_u)
 {
-	io_u_clear(td, io_u, IO_U_F_FLIGHT);
+	io_u_clear_inflight_flags(td, io_u);
 	put_io_u(td, io_u);
 }
 
@@ -2090,7 +2097,7 @@ static void io_completed(struct thread_data *td, struct io_u **io_u_ptr,
 	dprint_io_u(io_u, "complete");
 
 	assert(io_u->flags & IO_U_F_FLIGHT);
-	io_u_clear(td, io_u, IO_U_F_FLIGHT | IO_U_F_BUSY_OK | IO_U_F_PATTERN_DONE);
+	io_u_clear_inflight_flags(td, io_u);
 	invalidate_inflight(td, io_u);
 
 	if (td->o.zone_mode == ZONE_MODE_ZBD && td->o.recover_zbd_write_error &&
-- 
2.49.0