void write_iolog_put(struct thread_data *td, struct io_u *io_u)
{
- fprintf(td->iolog_f, "%d,%llu,%u\n", io_u->ddir, io_u->offset, io_u->buflen);
+ fprintf(td->iolog_f, "%u,%llu,%lu\n", io_u->ddir, io_u->offset, io_u->buflen);
}
int read_iolog_get(struct thread_data *td, struct io_u *io_u)
io_u->offset = ipo->offset;
io_u->buflen = ipo->len;
io_u->ddir = ipo->ddir;
+ io_u->file = ipo->file;
free(ipo);
return 0;
}
void prune_io_piece_log(struct thread_data *td)
{
struct io_piece *ipo;
+ struct rb_node *n;
- while (!list_empty(&td->io_hist_list)) {
- ipo = list_entry(td->io_hist_list.next, struct io_piece, list);
-
- list_del(&ipo->list);
+ while ((n = rb_first(&td->io_hist_tree)) != NULL) {
+ ipo = rb_entry(n, struct io_piece, rb_node);
+ rb_erase(n, &td->io_hist_tree);
free(ipo);
}
}
/*
- * log a succesful write, so we can unwind the log for verify
+ * log a successful write, so we can unwind the log for verify
*/
void log_io_piece(struct thread_data *td, struct io_u *io_u)
{
- struct io_piece *ipo = malloc(sizeof(struct io_piece));
- struct list_head *entry;
+ struct rb_node **p, *parent;
+ struct io_piece *ipo, *__ipo;
- INIT_LIST_HEAD(&ipo->list);
+ ipo = malloc(sizeof(struct io_piece));
+ ipo->file = io_u->file;
ipo->offset = io_u->offset;
ipo->len = io_u->buflen;
/*
- * for random io where the writes extend the file, it will typically
- * be laid out with the block scattered as written. it's faster to
- * read them in in that order again, so don't sort
+ * We don't need to sort the entries, if:
+ *
+ * Sequential writes, or
+ * Random writes that lay out the file as it goes along
+ *
+ * For both these cases, just reading back data in the order we
+ * wrote it out is the fastest.
*/
- if (td->sequential || !td->overwrite) {
+ if (!td_random(td) || !td->o.overwrite ||
+ (io_u->file->flags & FIO_FILE_NOSORT)) {
+ INIT_LIST_HEAD(&ipo->list);
list_add_tail(&ipo->list, &td->io_hist_list);
return;
}
+ RB_CLEAR_NODE(&ipo->rb_node);
+ p = &td->io_hist_tree.rb_node;
+ parent = NULL;
+
/*
- * for random io, sort the list so verify will run faster
+ * Sort the entry into the verification list
*/
- entry = &td->io_hist_list;
- while ((entry = entry->prev) != &td->io_hist_list) {
- struct io_piece *__ipo = list_entry(entry, struct io_piece, list);
-
- if (__ipo->offset < ipo->offset)
- break;
+ while (*p) {
+ parent = *p;
+
+ __ipo = rb_entry(parent, struct io_piece, rb_node);
+ if (ipo->offset <= __ipo->offset)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
}
- list_add(&ipo->list, entry);
+ rb_link_node(&ipo->rb_node, parent, p);
+ rb_insert_color(&ipo->rb_node, &td->io_hist_tree);
}
void write_iolog_close(struct thread_data *td)
free(td->iolog_buf);
}
-int init_iolog(struct thread_data *td)
+/*
+ * Open a stored log and read in the entries.
+ */
+static int init_iolog_read(struct thread_data *td)
{
unsigned long long offset;
unsigned int bytes;
char *str, *p;
FILE *f;
- int rw, i, reads, writes;
-
- if (!td->read_iolog && !td->write_iolog)
- return 0;
-
- if (td->read_iolog)
- f = fopen(td->iolog_file, "r");
- else
- f = fopen(td->iolog_file, "w");
+ int rw, reads, writes;
+ f = fopen(td->o.read_iolog_file, "r");
if (!f) {
- perror("fopen iolog");
- printf("file %s, %d/%d\n", td->iolog_file, td->read_iolog, td->write_iolog);
+ perror("fopen read iolog");
return 1;
}
- /*
- * That's it for writing, setup a log buffer and we're done.
- */
- if (td->write_iolog) {
- td->iolog_f = f;
- td->iolog_buf = malloc(8192);
- setvbuf(f, td->iolog_buf, _IOFBF, 8192);
- return 0;
- }
-
/*
* Read in the read iolog and store it, reuse the infrastructure
* for doing verifications.
*/
str = malloc(4096);
- reads = writes = i = 0;
+ reads = writes = 0;
while ((p = fgets(str, 4096, f)) != NULL) {
struct io_piece *ipo;
if (sscanf(p, "%d,%llu,%u", &rw, &offset, &bytes) != 3) {
- fprintf(stderr, "bad iolog: %s\n", p);
+ log_err("bad iolog: %s\n", p);
continue;
}
if (rw == DDIR_READ)
reads++;
else if (rw == DDIR_WRITE)
writes++;
- else {
- fprintf(stderr, "bad ddir: %d\n", rw);
+ else if (rw != DDIR_SYNC) {
+ log_err("bad ddir: %d\n", rw);
continue;
}
INIT_LIST_HEAD(&ipo->list);
ipo->offset = offset;
ipo->len = bytes;
- if (bytes > td->max_bs)
- td->max_bs = bytes;
- ipo->ddir = rw;
+ ipo->ddir = (enum fio_ddir) rw;
+ if (bytes > td->o.max_bs[rw])
+ td->o.max_bs[rw] = bytes;
list_add_tail(&ipo->list, &td->io_log_list);
- i++;
}
free(str);
fclose(f);
- if (!i)
+ if (!reads && !writes)
return 1;
-
- if (reads && !writes)
- td->ddir = DDIR_READ;
+ else if (reads && !writes)
+ td->o.td_ddir = TD_DDIR_READ;
else if (!reads && writes)
- td->ddir = DDIR_READ;
+ td->o.td_ddir = TD_DDIR_READ;
else
- td->iomix = 1;
+ td->o.td_ddir = TD_DDIR_RW;
return 0;
}
+
+/*
+ * Setup a log for storing io patterns.
+ */
+static int init_iolog_write(struct thread_data *td)
+{
+ FILE *f;
+
+ f = fopen(td->o.write_iolog_file, "w+");
+ if (!f) {
+ perror("fopen write iolog");
+ return 1;
+ }
+
+ /*
+ * That's it for writing, setup a log buffer and we're done.
+ */
+ td->iolog_f = f;
+ td->iolog_buf = malloc(8192);
+ setvbuf(f, td->iolog_buf, _IOFBF, 8192);
+ return 0;
+}
+
+int init_iolog(struct thread_data *td)
+{
+ int ret = 0;
+
+ if (td->io_ops->flags & FIO_DISKLESSIO)
+ return 0;
+
+ if (td->o.read_iolog_file)
+ ret = init_iolog_read(td);
+ else if (td->o.write_iolog_file)
+ ret = init_iolog_write(td);
+
+ return ret;
+}
+
+void setup_log(struct io_log **log)
+{
+ struct io_log *l = malloc(sizeof(*l));
+
+ l->nr_samples = 0;
+ l->max_samples = 1024;
+ l->log = malloc(l->max_samples * sizeof(struct io_sample));
+ *log = l;
+}
+
+void __finish_log(struct io_log *log, const char *name)
+{
+ unsigned int i;
+ FILE *f;
+
+ f = fopen(name, "w");
+ if (!f) {
+ perror("fopen log");
+ return;
+ }
+
+ for (i = 0; i < log->nr_samples; i++)
+ fprintf(f, "%lu, %lu, %u\n", log->log[i].time, log->log[i].val, log->log[i].ddir);
+
+ fclose(f);
+ free(log->log);
+ free(log);
+}
+
+void finish_log(struct thread_data *td, struct io_log *log, const char *name)
+{
+ char file_name[256];
+
+ snprintf(file_name, 200, "client%d_%s.log", td->thread_number, name);
+ __finish_log(log, file_name);
+}