return 1;
}
+ /*
+ * For randtrimwrite, we decide whether to issue a trim or a write
+ * based on whether the offsets for the most recent trim and write
+ * operations match. If they don't match that means we just issued a
+ * new trim and the next operation should be a write. If they *do*
+ * match that means we just completed a trim+write pair and the next
+ * command should be a trim.
+ *
+ * This works fine for sequential workloads but for random workloads
+ * it's possible to complete a trim+write pair and then have the next
+ * randomly generated offset match the previous offset. If that happens
+ * we need to alter the offset for the last write operation in order
+ * to ensure that we issue a write operation the next time through.
+ */
+ if (td_randtrimwrite(td) && ddir == DDIR_TRIM &&
+ f->last_start[DDIR_TRIM] == io_u->offset)
+ f->last_start[DDIR_WRITE]--;
+
io_u->verify_offset = io_u->offset;
return 0;
}
* check if the usec is capable of taking negative values
*/
if (now > td->o.timeout) {
- ddir = DDIR_INVAL;
+ ddir = DDIR_TIMEOUT;
return ddir;
}
usec = td->o.timeout - now;
now = utime_since_now(&td->epoch);
if ((td->o.timeout && (now > td->o.timeout)) || td->terminate)
- ddir = DDIR_INVAL;
+ ddir = DDIR_TIMEOUT;
return ddir;
}
else
ddir = DDIR_INVAL;
- td->rwmix_ddir = rate_ddir(td, ddir);
+ if (!should_check_rate(td)) {
+ /*
+ * avoid time-consuming call to utime_since_now() if rate checking
+ * isn't being used. this imrpoves IOPs 50%. See:
+ * https://github.com/axboe/fio/issues/1501#issuecomment-1418327049
+ */
+ td->rwmix_ddir = ddir;
+ } else
+ td->rwmix_ddir = rate_ddir(td, ddir);
return td->rwmix_ddir;
}
if (td_trimwrite(td)) {
struct fio_file *f = io_u->file;
- if (f->last_pos[DDIR_WRITE] == f->last_pos[DDIR_TRIM])
+ if (f->last_start[DDIR_WRITE] == f->last_start[DDIR_TRIM])
ddir = DDIR_TRIM;
else
ddir = DDIR_WRITE;
set_rw_ddir(td, io_u);
- if (io_u->ddir == DDIR_INVAL) {
+ if (io_u->ddir == DDIR_INVAL || io_u->ddir == DDIR_TIMEOUT) {
dprint(FD_IO, "invalid direction received ddir = %d", io_u->ddir);
return 1;
}
offset = io_u->offset;
if (td->o.zone_mode == ZONE_MODE_ZBD) {
ret = zbd_adjust_block(td, io_u);
- if (ret == io_u_eof)
+ if (ret == io_u_eof) {
+ dprint(FD_IO, "zbd_adjust_block() returned io_u_eof\n");
return 1;
+ }
}
+ if (td->o.fdp)
+ fdp_fill_dspec_data(td, io_u);
+
if (io_u->offset + io_u->buflen > io_u->file->real_file_size) {
dprint(FD_IO, "io_u %p, off=0x%llx + len=0x%llx exceeds file size=0x%llx\n",
io_u,
if (td->o.file_service_type == FIO_FSERVICE_SEQ)
goto out;
if (td->file_service_left) {
- td->file_service_left--;
- goto out;
+ td->file_service_left--;
+ goto out;
}
}
put_file_log(td, f);
td_io_close_file(td, f);
io_u->file = NULL;
+
+ if (io_u->ddir == DDIR_TIMEOUT)
+ return 1;
+
if (td->o.file_service_type & __FIO_FSERVICE_NONUNIFORM)
fio_file_reset(td, f);
else {
{
const bool needs_lock = td_async_processing(td);
struct io_u *io_u = NULL;
- int ret;
if (td->stop_io)
return NULL;
io_u_set(td, io_u, IO_U_F_IN_CUR_DEPTH);
io_u->ipo = NULL;
} else if (td_async_processing(td)) {
+ int ret;
/*
* We ran out, wait for async verify threads to finish and
* return one
*/
assert(!(td->flags & TD_F_CHILD));
ret = pthread_cond_wait(&td->free_cond, &td->io_u_lock);
- assert(ret == 0);
- if (!td->error)
+ if (fio_unlikely(ret != 0)) {
+ td->error = errno;
+ } else if (!td->error)
goto again;
}
io_ddir_name(io_u->ddir),
io_u->offset, io_u->xfer_buflen);
+ zbd_log_err(td, io_u);
+
if (td->io_ops->errdetails) {
char *err = td->io_ops->errdetails(io_u);
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_clear(td, io_u, IO_U_F_FLIGHT | IO_U_F_BUSY_OK | IO_U_F_PATTERN_DONE);
/*
* Mark IO ok to verify
}
if (ddir_sync(ddir)) {
+ if (io_u->error)
+ goto error;
td->last_was_sync = true;
if (f) {
f->first_write = -1ULL;
icd->error = ret;
}
} else if (io_u->error) {
+error:
icd->error = io_u->error;
io_u_log_error(td, io_u);
}
}
}
+static void io_u_update_bytes_done(struct thread_data *td,
+ struct io_completion_data *icd)
+{
+ int ddir;
+
+ if (td->runstate == TD_VERIFYING) {
+ td->bytes_verified += icd->bytes_done[DDIR_READ];
+ return;
+ }
+
+ for (ddir = 0; ddir < DDIR_RWDIR_CNT; ddir++)
+ td->bytes_done[ddir] += icd->bytes_done[ddir];
+}
+
/*
* Complete a single io_u for the sync engines.
*/
int io_u_sync_complete(struct thread_data *td, struct io_u *io_u)
{
struct io_completion_data icd;
- int ddir;
init_icd(td, &icd, 1);
io_completed(td, &io_u, &icd);
return -1;
}
- for (ddir = 0; ddir < DDIR_RWDIR_CNT; ddir++)
- td->bytes_done[ddir] += icd.bytes_done[ddir];
+ io_u_update_bytes_done(td, &icd);
return 0;
}
{
struct io_completion_data icd;
struct timespec *tvp = NULL;
- int ret, ddir;
+ int ret;
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0, };
dprint(FD_IO, "io_u_queued_complete: min=%d\n", min_evts);
return -1;
}
- for (ddir = 0; ddir < DDIR_RWDIR_CNT; ddir++)
- td->bytes_done[ddir] += icd.bytes_done[ddir];
+ io_u_update_bytes_done(td, &icd);
return ret;
}
return ret;
}
-int do_io_u_trim(const struct thread_data *td, struct io_u *io_u)
+int do_io_u_trim(struct thread_data *td, struct io_u *io_u)
{
#ifndef FIO_HAVE_TRIM
io_u->error = EINVAL;