iolog: convert drop warning to fio_did_warn()
[fio.git] / iolog.c
diff --git a/iolog.c b/iolog.c
index 0b775de13a21013995158cab0696340cff2db3c2..34e74a80a2f9ed2c278d6835f172acf650a4a9f9 100644 (file)
--- a/iolog.c
+++ b/iolog.c
@@ -19,6 +19,7 @@
 #include "trim.h"
 #include "filelock.h"
 #include "smalloc.h"
+#include "blktrace.h"
 
 static int iolog_flush(struct io_log *log);
 
@@ -64,7 +65,7 @@ static void iolog_delay(struct thread_data *td, unsigned long delay)
 {
        uint64_t usec = utime_since_now(&td->last_issue);
        uint64_t this_delay;
-       struct timeval tv;
+       struct timespec ts;
 
        if (delay < td->time_offset) {
                td->time_offset = 0;
@@ -77,7 +78,7 @@ static void iolog_delay(struct thread_data *td, unsigned long delay)
 
        delay -= usec;
 
-       fio_gettime(&tv, NULL);
+       fio_gettime(&ts, NULL);
        while (delay && !td->terminate) {
                this_delay = delay;
                if (this_delay > 500000)
@@ -87,7 +88,7 @@ static void iolog_delay(struct thread_data *td, unsigned long delay)
                delay -= this_delay;
        }
 
-       usec = utime_since_now(&tv);
+       usec = utime_since_now(&ts);
        if (usec > delay)
                td->time_offset = usec - delay;
        else
@@ -109,6 +110,11 @@ static int ipo_special(struct thread_data *td, struct io_piece *ipo)
 
        switch (ipo->file_action) {
        case FIO_LOG_OPEN_FILE:
+               if (td->o.replay_redirect && fio_file_open(f)) {
+                       dprint(FD_FILE, "iolog: ignoring re-open of file %s\n",
+                                       f->file_name);
+                       break;
+               }
                ret = td_io_open_file(td, f);
                if (!ret)
                        break;
@@ -221,21 +227,16 @@ void log_io_piece(struct thread_data *td, struct io_u *io_u)
        }
 
        /*
-        * We don't need to sort the entries, if:
-        *
-        *      Sequential writes, or
-        *      Random writes that lay out the file as it goes along
+        * We don't need to sort the entries if we only performed sequential
+        * writes. In this case, just reading back data in the order we wrote
+        * it out is the faster but still safe.
         *
-        * For both these cases, just reading back data in the order we
-        * wrote it out is the fastest.
-        *
-        * One exception is if we don't have a random map AND we are doing
-        * verifies, in that case we need to check for duplicate blocks and
-        * drop the old one, which we rely on the rb insert/lookup for
-        * handling.
+        * One exception is if we don't have a random map in which case we need
+        * to check for duplicate blocks and drop the old one, which we rely on
+        * the rb insert/lookup for handling.
         */
-       if (((!td->o.verifysort) || !td_random(td) || !td->o.overwrite) &&
-             (file_randommap(td, ipo->file) || td->o.verify == VERIFY_NONE)) {
+       if (((!td->o.verifysort) || !td_random(td)) &&
+             file_randommap(td, ipo->file)) {
                INIT_FLIST_HEAD(&ipo->list);
                flist_add_tail(&ipo->list, &td->io_hist_list);
                ipo->flags |= IP_F_ONLIST;
@@ -272,13 +273,14 @@ restart:
                        overlap = 1;
 
                if (overlap) {
-                       dprint(FD_IO, "iolog: overlap %llu/%lu, %llu/%lu",
+                       dprint(FD_IO, "iolog: overlap %llu/%lu, %llu/%lu\n",
                                __ipo->offset, __ipo->len,
                                ipo->offset, ipo->len);
                        td->io_hist_len--;
                        rb_erase(parent, &td->io_hist_tree);
                        remove_trim_entry(td, __ipo);
-                       free(__ipo);
+                       if (!(__ipo->flags & IP_F_IN_FLIGHT))
+                               free(__ipo);
                        goto restart;
                }
        }
@@ -346,7 +348,7 @@ static int read_iolog2(struct thread_data *td, FILE *f)
        unsigned long long offset;
        unsigned int bytes;
        int reads, writes, waits, fileno = 0, file_action = 0; /* stupid gcc */
-       char *fname, *act;
+       char *rfname, *fname, *act;
        char *str, *p;
        enum fio_ddir rw;
 
@@ -357,7 +359,7 @@ static int read_iolog2(struct thread_data *td, FILE *f)
         * for doing verifications.
         */
        str = malloc(4096);
-       fname = malloc(256+16);
+       rfname = fname = malloc(256+16);
        act = malloc(256+16);
 
        reads = writes = waits = 0;
@@ -365,8 +367,12 @@ static int read_iolog2(struct thread_data *td, FILE *f)
                struct io_piece *ipo;
                int r;
 
-               r = sscanf(p, "%256s %256s %llu %u", fname, act, &offset,
+               r = sscanf(p, "%256s %256s %llu %u", rfname, act, &offset,
                                                                        &bytes);
+
+               if (td->o.replay_redirect)
+                       fname = td->o.replay_redirect;
+
                if (r == 4) {
                        /*
                         * Check action first
@@ -392,8 +398,14 @@ static int read_iolog2(struct thread_data *td, FILE *f)
                } else if (r == 2) {
                        rw = DDIR_INVAL;
                        if (!strcmp(act, "add")) {
-                               fileno = add_file(td, fname, 0, 1);
-                               file_action = FIO_LOG_ADD_FILE;
+                               if (td->o.replay_redirect &&
+                                   get_fileno(td, fname) != -1) {
+                                       dprint(FD_FILE, "iolog: ignoring"
+                                               " re-add of file %s\n", fname);
+                               } else {
+                                       fileno = add_file(td, fname, 0, 1);
+                                       file_action = FIO_LOG_ADD_FILE;
+                               }
                                continue;
                        } else if (!strcmp(act, "open")) {
                                fileno = get_fileno(td, fname);
@@ -407,7 +419,7 @@ static int read_iolog2(struct thread_data *td, FILE *f)
                                continue;
                        }
                } else {
-                       log_err("bad iolog2: %s", p);
+                       log_err("bad iolog2: %s\n", p);
                        continue;
                }
 
@@ -421,6 +433,8 @@ static int read_iolog2(struct thread_data *td, FILE *f)
                                continue;
                        writes++;
                } else if (rw == DDIR_WAIT) {
+                       if (td->o.no_stall)
+                               continue;
                        waits++;
                } else if (rw == DDIR_INVAL) {
                } else if (!ddir_sync(rw)) {
@@ -437,7 +451,12 @@ static int read_iolog2(struct thread_data *td, FILE *f)
                if (rw == DDIR_WAIT) {
                        ipo->delay = offset;
                } else {
-                       ipo->offset = offset;
+                       if (td->o.replay_scale)
+                               ipo->offset = offset / td->o.replay_scale;
+                       else
+                               ipo->offset = offset;
+                       ipo_bytes_align(td->o.replay_align, ipo);
+
                        ipo->len = bytes;
                        if (rw != DDIR_INVAL && bytes > td->o.max_bs[rw])
                                td->o.max_bs[rw] = bytes;
@@ -451,7 +470,7 @@ static int read_iolog2(struct thread_data *td, FILE *f)
 
        free(str);
        free(act);
-       free(fname);
+       free(rfname);
 
        if (writes && read_only) {
                log_err("fio: <%s> skips replay of %d writes due to"
@@ -576,6 +595,9 @@ void setup_log(struct io_log **log, struct log_params *p,
               const char *filename)
 {
        struct io_log *l;
+       int i;
+       struct io_u_plat_entry *entry;
+       struct flist_head *list;
 
        l = scalloc(1, sizeof(*l));
        INIT_FLIST_HEAD(&l->io_logs);
@@ -584,9 +606,30 @@ void setup_log(struct io_log **log, struct log_params *p,
        l->log_gz = p->log_gz;
        l->log_gz_store = p->log_gz_store;
        l->avg_msec = p->avg_msec;
+       l->hist_msec = p->hist_msec;
+       l->hist_coarseness = p->hist_coarseness;
        l->filename = strdup(filename);
        l->td = p->td;
 
+       /* Initialize histogram lists for each r/w direction,
+        * with initial io_u_plat of all zeros:
+        */
+       for (i = 0; i < DDIR_RWDIR_CNT; i++) {
+               list = &l->hist_window[i].list;
+               INIT_FLIST_HEAD(list);
+               entry = calloc(1, sizeof(struct io_u_plat_entry));
+               flist_add(&entry->list, list);
+       }
+
+       if (l->td && l->td->o.io_submit_mode != IO_MODE_OFFLOAD) {
+               struct io_logs *p;
+
+               p = calloc(1, sizeof(*l->pending));
+               p->max_samples = DEF_LOG_ENTRIES;
+               p->log = calloc(p->max_samples, log_entry_sz(l));
+               l->pending = p;
+       }
+
        if (l->log_offset)
                l->log_ddir_mask = LOG_OFFSET_SAMPLE_BIT;
 
@@ -595,7 +638,8 @@ void setup_log(struct io_log **log, struct log_params *p,
        if (l->log_gz && !p->td)
                l->log_gz = 0;
        else if (l->log_gz || l->log_gz_store) {
-               pthread_mutex_init(&l->chunk_lock, NULL);
+               mutex_init_pshared(&l->chunk_lock);
+               mutex_init_pshared(&l->deferred_free_lock);
                p->td->flags |= TD_F_COMPRESS_LOG;
        }
 
@@ -636,12 +680,81 @@ void free_log(struct io_log *log)
                cur_log = flist_first_entry(&log->io_logs, struct io_logs, list);
                flist_del_init(&cur_log->list);
                free(cur_log->log);
+               sfree(cur_log);
+       }
+
+       if (log->pending) {
+               free(log->pending->log);
+               free(log->pending);
+               log->pending = NULL;
        }
 
+       free(log->pending);
        free(log->filename);
        sfree(log);
 }
 
+unsigned long hist_sum(int j, int stride, unsigned int *io_u_plat,
+               unsigned int *io_u_plat_last)
+{
+       unsigned long sum;
+       int k;
+
+       if (io_u_plat_last) {
+               for (k = sum = 0; k < stride; k++)
+                       sum += io_u_plat[j + k] - io_u_plat_last[j + k];
+       } else {
+               for (k = sum = 0; k < stride; k++)
+                       sum += io_u_plat[j + k];
+       }
+
+       return sum;
+}
+
+static void flush_hist_samples(FILE *f, int hist_coarseness, void *samples,
+                              uint64_t sample_size)
+{
+       struct io_sample *s;
+       int log_offset;
+       uint64_t i, j, nr_samples;
+       struct io_u_plat_entry *entry, *entry_before;
+       unsigned int *io_u_plat;
+       unsigned int *io_u_plat_before;
+
+       int stride = 1 << hist_coarseness;
+       
+       if (!sample_size)
+               return;
+
+       s = __get_sample(samples, 0, 0);
+       log_offset = (s->__ddir & LOG_OFFSET_SAMPLE_BIT) != 0;
+
+       nr_samples = sample_size / __log_entry_sz(log_offset);
+
+       for (i = 0; i < nr_samples; i++) {
+               s = __get_sample(samples, log_offset, i);
+
+               entry = s->data.plat_entry;
+               io_u_plat = entry->io_u_plat;
+
+               entry_before = flist_first_entry(&entry->list, struct io_u_plat_entry, list);
+               io_u_plat_before = entry_before->io_u_plat;
+
+               fprintf(f, "%lu, %u, %u, ", (unsigned long) s->time,
+                                               io_sample_ddir(s), s->bs);
+               for (j = 0; j < FIO_IO_U_PLAT_NR - stride; j += stride) {
+                       fprintf(f, "%lu, ", hist_sum(j, stride, io_u_plat,
+                                               io_u_plat_before));
+               }
+               fprintf(f, "%lu\n", (unsigned long)
+                       hist_sum(FIO_IO_U_PLAT_NR - stride, stride, io_u_plat,
+                                       io_u_plat_before));
+
+               flist_del(&entry_before->list);
+               free(entry_before);
+       }
+}
+
 void flush_samples(FILE *f, void *samples, uint64_t sample_size)
 {
        struct io_sample *s;
@@ -660,16 +773,16 @@ void flush_samples(FILE *f, void *samples, uint64_t sample_size)
                s = __get_sample(samples, log_offset, i);
 
                if (!log_offset) {
-                       fprintf(f, "%lu, %lu, %u, %u\n",
+                       fprintf(f, "%lu, %" PRId64 ", %u, %u\n",
                                        (unsigned long) s->time,
-                                       (unsigned long) s->val,
+                                       s->data.val,
                                        io_sample_ddir(s), s->bs);
                } else {
                        struct io_sample_offset *so = (void *) s;
 
-                       fprintf(f, "%lu, %lu, %u, %u, %llu\n",
+                       fprintf(f, "%lu, %" PRId64 ", %u, %u, %llu\n",
                                        (unsigned long) s->time,
-                                       (unsigned long) s->val,
+                                       s->data.val,
                                        io_sample_ddir(s), s->bs,
                                        (unsigned long long) so->offset);
                }
@@ -710,6 +823,7 @@ static int z_stream_init(z_stream *stream, int gz_hdr)
 {
        int wbits = 15;
 
+       memset(stream, 0, sizeof(*stream));
        stream->zalloc = Z_NULL;
        stream->zfree = Z_NULL;
        stream->opaque = Z_NULL;
@@ -744,7 +858,8 @@ static void finish_chunk(z_stream *stream, FILE *f,
 
        ret = inflateEnd(stream);
        if (ret != Z_OK)
-               log_err("fio: failed to end log inflation (%d)\n", ret);
+               log_err("fio: failed to end log inflation seq %d (%d)\n",
+                               iter->seq, ret);
 
        flush_samples(f, iter->buf, iter->buf_used);
        free(iter->buf);
@@ -761,7 +876,7 @@ static size_t inflate_chunk(struct iolog_compress *ic, int gz_hdr, FILE *f,
 {
        size_t ret;
 
-       dprint(FD_COMPRESS, "inflate chunk size=%lu, seq=%u",
+       dprint(FD_COMPRESS, "inflate chunk size=%lu, seq=%u\n",
                                (unsigned long) ic->len, ic->seq);
 
        if (ic->seq != iter->seq) {
@@ -808,7 +923,7 @@ static size_t inflate_chunk(struct iolog_compress *ic, int gz_hdr, FILE *f,
 
        ret = (void *) stream->next_in - ic->buf;
 
-       dprint(FD_COMPRESS, "inflated to size=%lu\n", (unsigned long) ret);
+       dprint(FD_COMPRESS, "inflated to size=%lu\n", (unsigned long) iter->buf_size);
 
        return ret;
 }
@@ -946,7 +1061,7 @@ int iolog_file_inflate(const char *file)
 
 #endif
 
-void flush_log(struct io_log *log, int do_append)
+void flush_log(struct io_log *log, bool do_append)
 {
        void *buf;
        FILE *f;
@@ -969,7 +1084,14 @@ void flush_log(struct io_log *log, int do_append)
 
                cur_log = flist_first_entry(&log->io_logs, struct io_logs, list);
                flist_del_init(&cur_log->list);
-               flush_samples(f, cur_log->log, cur_log->nr_samples * log_entry_sz(log));
+               
+               if (log->td && log == log->td->clat_hist_log)
+                       flush_hist_samples(f, log->hist_coarseness, cur_log->log,
+                                          log_sample_sz(log, cur_log));
+               else
+                       flush_samples(f, cur_log->log, log_sample_sz(log, cur_log));
+               
+               sfree(cur_log);
        }
 
        fclose(f);
@@ -1019,9 +1141,41 @@ size_t log_chunk_sizes(struct io_log *log)
 
 #ifdef CONFIG_ZLIB
 
+static void iolog_put_deferred(struct io_log *log, void *ptr)
+{
+       if (!ptr)
+               return;
+
+       pthread_mutex_lock(&log->deferred_free_lock);
+       if (log->deferred < IOLOG_MAX_DEFER) {
+               log->deferred_items[log->deferred] = ptr;
+               log->deferred++;
+       } else if (!fio_did_warn(FIO_WARN_IOLOG_DROP))
+               log_err("fio: had to drop log entry free\n");
+       pthread_mutex_unlock(&log->deferred_free_lock);
+}
+
+static void iolog_free_deferred(struct io_log *log)
+{
+       int i;
+
+       if (!log->deferred)
+               return;
+
+       pthread_mutex_lock(&log->deferred_free_lock);
+
+       for (i = 0; i < log->deferred; i++) {
+               free(log->deferred_items[i]);
+               log->deferred_items[i] = NULL;
+       }
+
+       log->deferred = 0;
+       pthread_mutex_unlock(&log->deferred_free_lock);
+}
+
 static int gz_work(struct iolog_flush_data *data)
 {
-       struct iolog_compress *c;
+       struct iolog_compress *c = NULL;
        struct flist_head list;
        unsigned int seq;
        z_stream stream;
@@ -1046,9 +1200,13 @@ static int gz_work(struct iolog_flush_data *data)
        stream.next_in = (void *) data->samples;
        stream.avail_in = data->nr_samples * log_entry_sz(data->log);
 
-       dprint(FD_COMPRESS, "deflate input size=%lu, seq=%u\n",
-                               (unsigned long) stream.avail_in, seq);
+       dprint(FD_COMPRESS, "deflate input size=%lu, seq=%u, log=%s\n",
+                               (unsigned long) stream.avail_in, seq,
+                               data->log->filename);
        do {
+               if (c)
+                       dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq,
+                               (unsigned long) c->len);
                c = get_new_chunk(seq);
                stream.avail_out = GZ_CHUNK;
                stream.next_out = c->buf;
@@ -1068,9 +1226,26 @@ static int gz_work(struct iolog_flush_data *data)
        stream.avail_out = GZ_CHUNK - c->len;
 
        ret = deflate(&stream, Z_FINISH);
-       if (ret == Z_STREAM_END)
-               c->len = GZ_CHUNK - stream.avail_out;
-       else {
+       if (ret < 0) {
+               /*
+                * Z_BUF_ERROR is special, it just means we need more
+                * output space. We'll handle that below. Treat any other
+                * error as fatal.
+                */
+               if (ret != Z_BUF_ERROR) {
+                       log_err("fio: deflate log (%d)\n", ret);
+                       flist_del(&c->list);
+                       free_chunk(c);
+                       goto err;
+               }
+       }
+
+       total -= c->len;
+       c->len = GZ_CHUNK - stream.avail_out;
+       total += c->len;
+       dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq, (unsigned long) c->len);
+
+       if (ret != Z_STREAM_END) {
                do {
                        c = get_new_chunk(seq);
                        stream.avail_out = GZ_CHUNK;
@@ -1079,6 +1254,8 @@ static int gz_work(struct iolog_flush_data *data)
                        c->len = GZ_CHUNK - stream.avail_out;
                        total += c->len;
                        flist_add_tail(&c->list, &list);
+                       dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq,
+                               (unsigned long) c->len);
                } while (ret != Z_STREAM_END);
        }
 
@@ -1088,7 +1265,7 @@ static int gz_work(struct iolog_flush_data *data)
        if (ret != Z_OK)
                log_err("fio: deflateEnd %d\n", ret);
 
-       free(data->samples);
+       iolog_put_deferred(data->log, data->samples);
 
        if (!flist_empty(&list)) {
                pthread_mutex_lock(&data->log->chunk_lock);
@@ -1099,7 +1276,7 @@ static int gz_work(struct iolog_flush_data *data)
        ret = 0;
 done:
        if (data->free)
-               free(data);
+               sfree(data);
        return ret;
 err:
        while (!flist_empty(&list)) {
@@ -1187,9 +1364,7 @@ static int iolog_flush(struct io_log *log)
                data->samples = cur_log->log;
                data->nr_samples = cur_log->nr_samples;
 
-               cur_log->nr_samples = 0;
-               cur_log->max_samples = 0;
-               cur_log->log = NULL;
+               sfree(cur_log);
 
                gz_work(data);
        }
@@ -1202,7 +1377,7 @@ int iolog_cur_flush(struct io_log *log, struct io_logs *cur_log)
 {
        struct iolog_flush_data *data;
 
-       data = malloc(sizeof(*data));
+       data = smalloc(sizeof(*data));
        if (!data)
                return 1;
 
@@ -1216,6 +1391,9 @@ int iolog_cur_flush(struct io_log *log, struct io_logs *cur_log)
        cur_log->log = NULL;
 
        workqueue_enqueue(&log->td->log_compress_wq, &data->work);
+
+       iolog_free_deferred(log);
+
        return 0;
 }
 #else
@@ -1314,6 +1492,20 @@ static int write_clat_log(struct thread_data *td, int try, bool unit_log)
        return ret;
 }
 
+static int write_clat_hist_log(struct thread_data *td, int try, bool unit_log)
+{
+       int ret;
+
+       if (!unit_log)
+               return 0;
+
+       ret = __write_log(td, td->clat_hist_log, try);
+       if (!ret)
+               td->clat_hist_log = NULL;
+
+       return ret;
+}
+
 static int write_lat_log(struct thread_data *td, int try, bool unit_log)
 {
        int ret;
@@ -1348,8 +1540,9 @@ enum {
        SLAT_LOG_MASK   = 4,
        CLAT_LOG_MASK   = 8,
        IOPS_LOG_MASK   = 16,
+       CLAT_HIST_LOG_MASK = 32,
 
-       ALL_LOG_NR      = 5,
+       ALL_LOG_NR      = 6,
 };
 
 struct log_type {
@@ -1378,6 +1571,10 @@ static struct log_type log_types[] = {
                .mask   = IOPS_LOG_MASK,
                .fn     = write_iops_log,
        },
+       {
+               .mask   = CLAT_HIST_LOG_MASK,
+               .fn     = write_clat_hist_log,
+       }
 };
 
 void td_writeout_logs(struct thread_data *td, bool unit_logs)