+ flist_add_tail(&ipo->list, &td->io_log_list);
+ td->total_io_size += ipo->len;
+}
+
+void log_io_u(struct thread_data *td, struct io_u *io_u)
+{
+ const char *act[] = { "read", "write", "sync", "datasync",
+ "sync_file_range", "wait", "trim" };
+
+ assert(io_u->ddir <= 6);
+
+ if (!td->o.write_iolog_file)
+ return;
+
+ fprintf(td->iolog_f, "%s %s %llu %lu\n", io_u->file->file_name,
+ act[io_u->ddir], io_u->offset,
+ io_u->buflen);
+}
+
+void log_file(struct thread_data *td, struct fio_file *f,
+ enum file_log_act what)
+{
+ const char *act[] = { "add", "open", "close" };
+
+ assert(what < 3);
+
+ if (!td->o.write_iolog_file)
+ return;
+
+
+ /*
+ * this happens on the pre-open/close done before the job starts
+ */
+ if (!td->iolog_f)
+ return;
+
+ fprintf(td->iolog_f, "%s %s\n", f->file_name, act[what]);
+}
+
+static void iolog_delay(struct thread_data *td, unsigned long delay)
+{
+ unsigned long usec = utime_since_now(&td->last_issue);
+
+ if (delay < usec)
+ return;
+
+ delay -= usec;
+
+ /*
+ * less than 100 usec delay, just regard it as noise
+ */
+ if (delay < 100)
+ return;
+
+ usec_sleep(td, delay);
+}
+
+static int ipo_special(struct thread_data *td, struct io_piece *ipo)
+{
+ struct fio_file *f;
+ int ret;
+
+ /*
+ * Not a special ipo
+ */
+ if (ipo->ddir != DDIR_INVAL)
+ return 0;
+
+ f = td->files[ipo->fileno];
+
+ switch (ipo->file_action) {
+ case FIO_LOG_OPEN_FILE:
+ ret = td_io_open_file(td, f);
+ if (!ret)
+ break;
+ td_verror(td, ret, "iolog open file");
+ return -1;
+ case FIO_LOG_CLOSE_FILE:
+ td_io_close_file(td, f);
+ break;
+ case FIO_LOG_UNLINK_FILE:
+ unlink(f->file_name);
+ break;
+ default:
+ log_err("fio: bad file action %d\n", ipo->file_action);
+ break;
+ }
+
+ return 1;