Recent changes (master)
Jens Axboe <[email protected]> Thu, 21 May 2026 06:00:01 -0600
| Newsgroups | org.kernel.vger.fio |
|---|---|
| Message-ID | <[email protected]> |
The following changes since commit 4de5b267a716a9b7b71b212e0b948743f70c4d54:
Merge branch 'pr-free-td-init' of https://github.com/malikoyv/fio (2026-05-15 08:03:55 -0600)
are available in the Git repository at:
git://git.kernel.dk/fio.git master
for you to fetch changes up to d569883e281076e0dec5f5768b828ef44392a11a:
Merge branch 'skip-errored-offset' of https://github.com/minwooim/fio (2026-05-20 09:17:25 -0400)
----------------------------------------------------------------
Minwoo Im (1):
verify: skip errored WRITE offset in verify
Vincent Fu (2):
ci: run all non-QEMU tests on push/PR/schedule
Merge branch 'skip-errored-offset' of https://github.com/minwooim/fio
backend.c | 58 ++++++++++++++++++++++++++++--
ci/actions-full-test.sh | 16 ---------
fio.h | 7 ++++
io_u.c | 23 ++++++++++++
verify-state.h | 3 +-
verify.c | 95 ++++++++++++++++++++++++++++++++++++++++++++-----
6 files changed, 173 insertions(+), 29 deletions(-)
---
Diff of recent changes:
diff --git a/backend.c b/backend.c
index 7a02ea11..f6023d0e 100644
--- a/backend.c
+++ b/backend.c
@@ -621,6 +621,7 @@ static enum fio_q_status io_u_submit(struct thread_data *td, struct io_u *io_u)
*/
static void do_verify(struct thread_data *td, uint64_t verify_bytes)
{
+ uint64_t expected_numberio;
struct fio_file *f;
struct io_u *io_u;
unsigned int i;
@@ -649,6 +650,7 @@ static void do_verify(struct thread_data *td, uint64_t verify_bytes)
td_set_runstate(td, TD_VERIFYING);
io_u = NULL;
+ expected_numberio = UINT64_MAX;
while (!td->terminate) {
enum fio_ddir ddir;
int full;
@@ -677,6 +679,33 @@ static void do_verify(struct thread_data *td, uint64_t verify_bytes)
break;
}
+ /*
+ * Advance verify_state to the seed that was used when
+ * this block was written. Any numberio gap between
+ * @expected_numberio and @io_u->numberio represents
+ * skipped writes due to errors (e.g., suppressed by
+ * --ignore_error= in online verification, or skipped
+ * offsets from do_dry_run in offline verification).
+ */
+ if (td->o.verify_header_seed && !td->o.verify_pattern_bytes) {
+ uint64_t seed = 0;
+ uint64_t from;
+
+ if (io_u->numberio < expected_numberio)
+ from = io_u->numberio;
+ else
+ from = expected_numberio;
+
+ for (uint64_t n = from; n <= io_u->numberio; n++) {
+ seed = __rand(&td->verify_state);
+ if (sizeof(int) != sizeof(long *))
+ seed *= __rand(&td->verify_state);
+ }
+
+ io_u->rand_seed = seed;
+ expected_numberio = io_u->numberio + 1;
+ }
+
if (td_io_prep(td, io_u)) {
put_io_u(td, io_u);
break;
@@ -1207,7 +1236,26 @@ static void do_io(struct thread_data *td, uint64_t *bytes_done)
io_u->rand_seed *= __rand(&td->verify_state);
}
- if (verify_state_should_stop(td, td->io_issues[io_u->ddir])) {
+ /*
+ * Assign numberio for read-only verify workloads. This
+ * handles two cases: (1) rw=[rand]write with verify_only=1
+ * and verify_state_load=1, where we replay a prior write
+ * run; (2) a prior job ran with rw=[rand]write and
+ * verify_state_save=1, and the current job runs with
+ * rw=[rand]read and verify_state_load=1 to verify those
+ * writes.
+ */
+ if (!td_rw(td) && !(io_u->flags & IO_U_F_VER_LIST))
+ io_u->numberio = td->io_issues[io_u->ddir];
+
+ if (verify_state_should_skip(td, io_u->numberio)) {
+ /* Account for this I/O so we move to the next sequence */
+ td->io_issues[io_u->ddir]++;
+ put_io_u(td, io_u);
+ continue;
+ }
+
+ if (verify_state_should_stop(td, io_u->numberio)) {
put_io_u(td, io_u);
break;
}
@@ -1380,6 +1428,8 @@ static void free_inflight_logging(struct thread_data *td)
{
if (td->inflight_numberio)
sfree(td->inflight_numberio);
+ if (td->failed_numberio)
+ free(td->failed_numberio);
}
static void cleanup_io_u(struct thread_data *td)
@@ -1780,8 +1830,10 @@ static uint64_t do_dry_run(struct thread_data *td)
if (td_write(td) && io_u->ddir == DDIR_WRITE &&
td->o.do_verify &&
td->o.verify != VERIFY_NONE &&
- !td->o.experimental_verify)
- log_io_piece(td, io_u);
+ !td->o.experimental_verify) {
+ if (!verify_state_should_skip(td, io_u->numberio))
+ log_io_piece(td, io_u);
+ }
ret = io_u_sync_complete(td, io_u);
(void) ret;
diff --git a/ci/actions-full-test.sh b/ci/actions-full-test.sh
index 14c2fcf7..56b5b31f 100755
--- a/ci/actions-full-test.sh
+++ b/ci/actions-full-test.sh
@@ -40,22 +40,6 @@ main() {
fi
- # If we are running a nightly test just run the verify tests. Skip the
- # verify test script with pull requests and pushes because it takes so
- # long. When this workflow is run manually everything will be run.
- if [ "${GITHUB_EVENT_NAME}" == "schedule" ]; then
- args+=(
- --run-only
- 1017
- -p
- "1017:--complete"
- )
- elif [ "${GITHUB_EVENT_NAME}" == "pull_request" ] || [ "${GITHUB_EVENT_NAME}" == "push" ]; then
- skip+=(
- 1017
- )
- fi
-
echo python3 t/run-fio-tests.py --skip "${skip[@]}" "${args[@]}"
python3 t/run-fio-tests.py -c --skip "${skip[@]}" "${args[@]}"
make -C doc html
diff --git a/fio.h b/fio.h
index 18196123..3b6ff6c6 100644
--- a/fio.h
+++ b/fio.h
@@ -386,6 +386,13 @@ struct thread_data {
unsigned int next_inflight_numberio_idx;
uint64_t inflight_issued;
+ /*
+ * Track failed write I/Os for offline verification (to exclude from verify state)
+ */
+ uint64_t *failed_numberio;
+ unsigned int failed_numberio_count;
+ unsigned int failed_numberio_alloc;
+
/*
* Completions
*/
diff --git a/io_u.c b/io_u.c
index 24d8b7de..1725a9ce 100644
--- a/io_u.c
+++ b/io_u.c
@@ -2137,6 +2137,29 @@ static void io_completed(struct thread_data *td, struct io_u **io_u_ptr,
!td_ioengine_flagged(td, FIO_SYNCIO))
zbd_recover_write_error(td, io_u);
+ /*
+ * Track failed write I/Os for offline verification (independent of io_u->ipo).
+ * This works even when do_verify=0 is set. In case of online
+ * verification, `unlog_io_piece()` will be called so that the ipo will
+ * be removed from the `io_hist`.
+ */
+ if (io_u->error && ddir == DDIR_WRITE && io_u->numberio != INVALID_NUMBERIO) {
+ if (td->failed_numberio_count >= td->failed_numberio_alloc) {
+ unsigned int new_alloc;
+
+ if (td->failed_numberio_alloc)
+ new_alloc = td->failed_numberio_alloc * 2;
+ else
+ new_alloc = 16;
+ td->failed_numberio = realloc(td->failed_numberio,
+ new_alloc * sizeof(uint64_t));
+ td->failed_numberio_alloc = new_alloc;
+ }
+ td->failed_numberio[td->failed_numberio_count++] = io_u->numberio;
+ dprint(FD_VERIFY, "Recorded failed write numberio=%"PRIu64"\n",
+ io_u->numberio);
+ }
+
/*
* Mark IO ok to verify
*/
diff --git a/verify-state.h b/verify-state.h
index 27eb9e9a..03093ecd 100644
--- a/verify-state.h
+++ b/verify-state.h
@@ -41,7 +41,7 @@ struct all_io_list {
struct thread_io_list state[0];
};
-#define VSTATE_HDR_VERSION 0x05
+#define VSTATE_HDR_VERSION 0x06
struct verify_state_hdr {
uint64_t version;
@@ -57,6 +57,7 @@ extern void __verify_save_state(struct all_io_list *, const char *);
extern void verify_save_state(int mask);
extern int verify_load_state(struct thread_data *, const char *);
extern void verify_free_state(struct thread_data *);
+extern int verify_state_should_skip(struct thread_data *, uint64_t);
extern int verify_state_should_stop(struct thread_data *, uint64_t);
extern void verify_assign_state(struct thread_data *, void *);
extern int verify_state_hdr(struct verify_state_hdr *, struct thread_io_list *);
diff --git a/verify.c b/verify.c
index 76da89eb..9dd9da7e 100644
--- a/verify.c
+++ b/verify.c
@@ -1474,11 +1474,6 @@ int get_next_verify(struct thread_data *td, struct io_u *io_u)
free(ipo);
dprint(FD_VERIFY, "get_next_verify: ret io_u %p\n", io_u);
- if (!td->o.verify_pattern_bytes) {
- io_u->rand_seed = __rand(&td->verify_state);
- if (sizeof(int) != sizeof(long *))
- io_u->rand_seed *= __rand(&td->verify_state);
- }
return 0;
}
@@ -1655,7 +1650,8 @@ struct all_io_list *get_all_io_list(int save_mask, size_t *sz)
continue;
td->stop_io = 1;
td->flags |= TD_F_VSTATE_SAVED;
- depth += (td->o.iodepth * td->o.nr_files);
+ /* Include both inflight and failed I/Os in the state */
+ depth += ((td->o.iodepth + td->failed_numberio_count) * td->o.nr_files);
nr++;
} end_for_each();
@@ -1672,6 +1668,7 @@ struct all_io_list *get_all_io_list(int save_mask, size_t *sz)
next = &rep->state[0];
for_each_td(td) {
struct thread_io_list *s = next;
+ unsigned int total_depth;
if (save_mask != IO_LIST_ALL && (__td_index + 1) != save_mask)
continue;
@@ -1685,7 +1682,15 @@ struct all_io_list *get_all_io_list(int save_mask, size_t *sz)
for (int i = 0; td->inflight_numberio && i < td->o.iodepth; i++)
s->inflight[i].numberio = cpu_to_le64(atomic_load_acquire(&td->inflight_numberio[i]));
- s->depth = cpu_to_le32((uint32_t) td->o.iodepth);
+ /* Then, append failed I/Os to exclude them from verification */
+ for (unsigned int i = 0; i < td->failed_numberio_count; i++) {
+ s->inflight[td->o.iodepth + i].numberio = cpu_to_le64(td->failed_numberio[i]);
+ dprint(FD_VERIFY, "Added failed numberio=%"PRIu64" to inflight list\n",
+ td->failed_numberio[i]);
+ }
+
+ total_depth = td->o.iodepth + td->failed_numberio_count;
+ s->depth = cpu_to_le32((uint32_t) total_depth);
s->numberio = cpu_to_le64((uint64_t) atomic_load_acquire(&td->inflight_issued));
s->index = cpu_to_le64((uint64_t) __td_index);
if (td->offset_state.use64) {
@@ -1809,6 +1814,18 @@ void verify_free_state(struct thread_data *td)
free(td->vstate);
}
+static int verify_u64_cmp(const void *a, const void *b)
+{
+ uint64_t x = *(const uint64_t *) a;
+ uint64_t y = *(const uint64_t *) b;
+
+ if (x < y)
+ return -1;
+ if (x > y)
+ return 1;
+ return 0;
+}
+
void verify_assign_state(struct thread_data *td, void *p)
{
struct thread_io_list *s = p;
@@ -1831,6 +1848,30 @@ void verify_assign_state(struct thread_data *td, void *p)
dprint(FD_VERIFY, "verify_assign_state numberio=%"PRIu64", inflight[%d]=%"PRIu64"\n", s->numberio, i, s->inflight[i].numberio);
}
+ /*
+ * Restore failed I/Os from state. Failed I/Os are appended after
+ * the regular inflight array (starting at index td->o.iodepth).
+ */
+ if (s->depth > td->o.iodepth) {
+ unsigned int failed_count = s->depth - td->o.iodepth;
+
+ td->failed_numberio = malloc(failed_count * sizeof(uint64_t));
+ td->failed_numberio_alloc = failed_count;
+ td->failed_numberio_count = 0;
+
+ for (i = td->o.iodepth; i < s->depth; i++) {
+ uint64_t nio = s->inflight[i].numberio;
+ if (nio != INVALID_NUMBERIO) {
+ td->failed_numberio[td->failed_numberio_count++] = nio;
+ dprint(FD_VERIFY, "Restored failed numberio=%"PRIu64"\n", nio);
+ }
+ }
+
+ /* Sort for O(log n) binary search in verify_state_should_skip() */
+ qsort(td->failed_numberio, td->failed_numberio_count,
+ sizeof(uint64_t), verify_u64_cmp);
+ }
+
td->vstate = p;
}
@@ -1911,6 +1952,35 @@ err:
return 1;
}
+/*
+ * Check if this I/O should be skipped during verification (because it failed during write).
+ * failed_numberio[] is sorted in verify_assign_state(), so we use binary search: O(log n).
+ */
+int verify_state_should_skip(struct thread_data *td, uint64_t numberio)
+{
+ unsigned int lo, hi;
+
+ if (!td->failed_numberio || td->failed_numberio_count == 0)
+ return 0;
+
+ lo = 0;
+ hi = td->failed_numberio_count;
+ while (lo < hi) {
+ unsigned int mid = lo + (hi - lo) / 2;
+
+ if (td->failed_numberio[mid] == numberio) {
+ dprint(FD_VERIFY, "Skipping failed numberio=%"PRIu64"\n", numberio);
+ return 1;
+ } else if (td->failed_numberio[mid] < numberio) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
+ }
+
+ return 0;
+}
+
/*
* Use the loaded verify state to know when to stop doing verification
*/
@@ -1924,10 +1994,17 @@ int verify_state_should_stop(struct thread_data *td, uint64_t numberio)
return 0;
/* If the current seq is lower than the max issued seq, check to make sure
- * the write was not inflight.
+ * the write was not inflight (but exclude failed writes, they should be skipped not stopped).
*/
if (numberio < s->numberio) {
- for (i = 0; i < s->depth; i++) {
+ /* Check only the actual inflight array (first td->o.iodepth entries) */
+ int actual_depth;
+
+ if (s->depth > td->o.iodepth)
+ actual_depth = td->o.iodepth;
+ else
+ actual_depth = s->depth;
+ for (i = 0; i < actual_depth; i++) {
if (s->inflight[i].numberio == numberio) {
log_info("Stop verify because seq %"PRIu64" was an inflight write\n",
numberio);