blktrace support: speedup reading of data

We used to read in data in really small chunks (48 bytes at the time,
the size of the trace). This is really slow for large traces, so
add a fifo frontend to refill the cache in much larger sizes.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/Makefile b/Makefile
index 5858ab0..9effdaa 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@
 SCRIPTS = fio_generate_plots
 OBJS = gettime.o fio.o ioengines.o init.o stat.o log.o time.o md5.o crc32.o \
 	filesetup.o eta.o verify.o memory.o io_u.o parse.o mutex.o options.o \
-	rbtree.o diskutil.o blktrace.o
+	rbtree.o diskutil.o fifo.o blktrace.o
 
 OBJS += engines/cpu.o
 OBJS += engines/libaio.o
diff --git a/blktrace.c b/blktrace.c
index afa699c..fc98091 100644
--- a/blktrace.c
+++ b/blktrace.c
@@ -8,6 +8,59 @@
 #include "fio.h"
 #include "blktrace_api.h"
 
+#define TRACE_FIFO_SIZE	(sizeof(struct blk_io_trace) * 1000)
+
+/*
+ * fifo refill frontend, to avoid reading data in trace sized bites
+ */
+static int refill_fifo(struct thread_data *td, struct fifo *fifo, int fd)
+{
+	char buf[TRACE_FIFO_SIZE];
+	unsigned int total, left;
+	void *ptr;
+	int ret;
+
+	total = 0;
+	ptr = buf;
+	while (total < TRACE_FIFO_SIZE) {
+		left = TRACE_FIFO_SIZE - total;
+
+		ret = read(fd, ptr, left);
+		if (ret < 0) {
+			td_verror(td, errno, "read blktrace file");
+			return -1;
+		} else if (!ret)
+			break;
+
+		fifo_put(fifo, ptr, ret);
+		ptr += ret;
+		total += ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Retrieve 'len' bytes from the fifo, refilling if necessary.
+ */
+static int trace_fifo_get(struct thread_data *td, struct fifo *fifo, int fd,
+			  void *buf, unsigned int len)
+{
+	int ret;
+
+	if (fifo_len(fifo) >= len)
+		return fifo_get(fifo, buf, len);
+
+	ret = refill_fifo(td, fifo, fd);
+	if (ret < 0)
+		return ret;
+
+	if (fifo_len(fifo) < len)
+		return 0;
+
+	return fifo_get(fifo, buf, len);
+}
+
 /*
  * Just discard the pdu by seeking past it.
  */
@@ -120,6 +173,7 @@
 	unsigned long ios[2];
 	unsigned int cpu;
 	unsigned int rw_bs[2];
+	struct fifo *fifo;
 	int fd;
 
 	fd = open(filename, O_RDONLY);
@@ -128,6 +182,8 @@
 		return 1;
 	}
 
+	fifo = fifo_alloc(TRACE_FIFO_SIZE);
+
 	td->o.size = 0;
 
 	cpu = 0;
@@ -135,22 +191,15 @@
 	ios[0] = ios[1] = 0;
 	rw_bs[0] = rw_bs[1] = 0;
 	do {
-		/*
-		 * Once this is working fully, I'll add a layer between
-		 * here and read to cache trace data. Then we can avoid
-		 * doing itsy bitsy reads, but instead pull in a larger
-		 * chunk of data at the time.
-		 */
-		int ret = read(fd, &t, sizeof(t));
+		int ret = trace_fifo_get(td, fifo, fd, &t, sizeof(t));
 
-		if (ret < 0) {
-			td_verror(td, errno, "read blktrace file");
+		if (ret < 0)
 			goto err;
-		} else if (!ret) {
+		else if (!ret)
 			break;
-		} else if (ret != sizeof(t)) {
-			log_err("fio: short read on blktrace file\n");
-			goto err;
+		else if (ret < (int) sizeof(t)) {
+			log_err("fio: short fifo get\n");
+			break;
 		}
 
 		if ((t.magic & 0xffffff00) != BLK_IO_TRACE_MAGIC) {
diff --git a/fifo.c b/fifo.c
new file mode 100644
index 0000000..1e55b63
--- /dev/null
+++ b/fifo.c
@@ -0,0 +1,88 @@
+/*
+ * A simple kernel FIFO implementation.
+ *
+ * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fifo.h"
+
+struct fifo *fifo_alloc(unsigned int size)
+{
+	struct fifo *fifo;
+
+	fifo = malloc(sizeof(struct fifo));
+	if (!fifo)
+		return 0;
+
+	fifo->buffer = malloc(size);
+	fifo->size = size;
+	fifo->in = fifo->out = 0xffff0000;
+
+	return fifo;
+}
+
+void fifo_free(struct fifo *fifo)
+{
+	free(fifo->buffer);
+	free(fifo);
+}
+
+unsigned int fifo_put(struct fifo *fifo, void *buffer, unsigned int len)
+{
+	unsigned int l;
+
+	len = min(len, fifo->size - fifo->in + fifo->out);
+
+	/* first put the data starting from fifo->in to buffer end */
+	l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
+	memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
+
+	/* then put the rest (if any) at the beginning of the buffer */
+	memcpy(fifo->buffer, buffer + l, len - l);
+
+	/*
+	 * Ensure that we add the bytes to the fifo -before-
+	 * we update the fifo->in index.
+	 */
+
+	fifo->in += len;
+
+	return len;
+}
+
+unsigned int fifo_get(struct fifo *fifo, void *buffer, unsigned int len)
+{
+	unsigned int l;
+
+	len = min(len, fifo->in - fifo->out);
+
+	/* first get the data from fifo->out until the end of the buffer */
+	l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
+	memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
+
+	/* then get the rest (if any) from the beginning of the buffer */
+	memcpy(buffer + l, fifo->buffer, len - l);
+
+	fifo->out += len;
+
+	return len;
+}
diff --git a/fifo.h b/fifo.h
new file mode 100644
index 0000000..6a9115d
--- /dev/null
+++ b/fifo.h
@@ -0,0 +1,48 @@
+/*
+ * A simple FIFO implementation.
+ *
+ * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+struct fifo {
+	unsigned char *buffer;	/* the buffer holding the data */
+	unsigned int size;	/* the size of the allocated buffer */
+	unsigned int in;	/* data is added at offset (in % size) */
+	unsigned int out;	/* data is extracted from off. (out % size) */
+};
+
+struct fifo *fifo_alloc(unsigned int);
+unsigned int fifo_put(struct fifo *, void *, unsigned int);
+unsigned int fifo_get(struct fifo *, void *, unsigned int);
+
+static inline unsigned int fifo_len(struct fifo *fifo)
+{
+	return fifo->in - fifo->out;
+}
+
+#define min(x,y) ({ \
+	typeof(x) _x = (x);	\
+	typeof(y) _y = (y);	\
+	(void) (&_x == &_y);		\
+	_x < _y ? _x : _y; })
+
+#define max(x,y) ({ \
+	typeof(x) _x = (x);	\
+	typeof(y) _y = (y);	\
+	(void) (&_x == &_y);		\
+	_x > _y ? _x : _y; })
+
diff --git a/fio.h b/fio.h
index fa0edb1..bcd188b 100644
--- a/fio.h
+++ b/fio.h
@@ -15,6 +15,7 @@
 
 #include "compiler/compiler.h"
 #include "list.h"
+#include "fifo.h"
 #include "rbtree.h"
 #include "md5.h"
 #include "crc32.h"
@@ -665,13 +666,6 @@
 
 #define DISK_UTIL_MSEC	(250)
 
-#ifndef min
-#define min(a, b)	((a) < (b) ? (a) : (b))
-#endif
-#ifndef max
-#define max(a, b)	((a) > (b) ? (a) : (b))
-#endif
-
 /*
  * Log exports
  */