[PATCH] Link in known io engines

No real point in using dlopen() to find engines we know about. We still
support loading external modules, just give the name as the full path
to such a file.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/Makefile b/Makefile
index ad9aa3b..1ebad6a 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,14 @@
 OBJS = 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
 
+OBJS += engines/fio-engine-cpu.o
+OBJS += engines/fio-engine-libaio.o
+OBJS += engines/fio-engine-mmap.o
+OBJS += engines/fio-engine-posixaio.o
+OBJS += engines/fio-engine-sg.o
+OBJS += engines/fio-engine-splice.o
+OBJS += engines/fio-engine-sync.o
+
 INSTALL = install
 prefix = /usr/local
 bindir = $(prefix)/bin
@@ -15,10 +23,9 @@
 CFLAGS += '-D_INST_PREFIX="$(FIO_INST_DIR)"'
 
 all: depend $(PROGS) $(SCRIPTS)
-	@$(MAKE) -C engines
 
 fio: $(OBJS)
-	$(CC) $(CFLAGS) -o $@ $(filter %.o,$^) -lpthread -lm -ldl
+	$(CC) $(CFLAGS) -o $@ $(filter %.o,$^) -lpthread -lm -ldl -laio -lrt
 
 clean:
 	-rm -f *.o .depend cscope.out $(PROGS) engines/*.o
@@ -33,7 +40,6 @@
 	$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
 	$(INSTALL) $(PROGS) $(SCRIPTS) $(DESTDIR)$(bindir)
 	$(INSTALL) -m755 -d $(DESTDIR) $(libdir)
-	$(INSTALL) engines/*.o $(libdir)
 
 ifneq ($(wildcard .depend),)
 include .depend
diff --git a/engines/Makefile b/engines/Makefile
deleted file mode 100644
index 963a93e..0000000
--- a/engines/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-CC	= gcc
-CFLAGS	= -W -Wall -O2 -g -shared -rdynamic -fPIC
-ALL_CFLAGS = $(CFLAGS) -I.. -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
-OBJS	= fio-engine-sync.o fio-engine-splice.o fio-engine-mmap.o fio-engine-libaio.o fio-engine-posixaio.o fio-engine-sg.o fio-engine-cpu.o
-
-all: depend $(OBJS)
-
-depend:
-	@$(CC) -MM $(ALL_CFLAGS) *.c 1> .depend
-
-clean:
-	-rm -f *.o $(OBJS) .depend
-
-%.o: %.c
-	$(CC) $(ALL_CFLAGS) -o $*.o $<
-
-fio-engine-libaio.o: fio-engine-libaio.c
-	$(CC) $(ALL_CFLAGS) -laio -o $*.o $<
-
-fio-engine-posixaio.o: fio-engine-posixaio.c
-	$(CC) $(ALL_CFLAGS) -lrt -o $*.o $<
-
-ifneq ($(wildcard .depend),)
-include .depend
-endif
diff --git a/engines/fio-engine-cpu.c b/engines/fio-engine-cpu.c
index 4ba12c6..f65f91d 100644
--- a/engines/fio-engine-cpu.c
+++ b/engines/fio-engine-cpu.c
@@ -1,5 +1,5 @@
-#include "fio.h"
-#include "os.h"
+#include "../fio.h"
+#include "../os.h"
 
 static int fio_cpuio_setup(struct thread_data fio_unused *td)
 {
@@ -19,10 +19,20 @@
 	return 0;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "cpuio",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_cpuio_init,
 	.setup		= fio_cpuio_setup,
 	.flags		= FIO_CPUIO,
 };
+
+static void fio_init fio_cpuio_register(void)
+{
+	register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_cpuio_unregister(void)
+{
+	unregister_ioengine(&ioengine);
+}
diff --git a/engines/fio-engine-libaio.c b/engines/fio-engine-libaio.c
index 12ddc98..45e69e9 100644
--- a/engines/fio-engine-libaio.c
+++ b/engines/fio-engine-libaio.c
@@ -7,8 +7,9 @@
 #include <unistd.h>
 #include <errno.h>
 #include <assert.h>
-#include "fio.h"
-#include "os.h"
+
+#include "../fio.h"
+#include "../os.h"
 
 #ifdef FIO_HAVE_LIBAIO
 
@@ -130,7 +131,7 @@
 	return 0;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "libaio",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_libaio_init,
@@ -155,10 +156,20 @@
 	return 1;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "libaio",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_libaio_init,
 };
 
 #endif
+
+static void fio_init fio_libaio_register(void)
+{
+	register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_libaio_unregister(void)
+{
+	unregister_ioengine(&ioengine);
+}
diff --git a/engines/fio-engine-mmap.c b/engines/fio-engine-mmap.c
index 483a704..20dcfd2 100644
--- a/engines/fio-engine-mmap.c
+++ b/engines/fio-engine-mmap.c
@@ -8,8 +8,9 @@
 #include <errno.h>
 #include <assert.h>
 #include <sys/mman.h>
-#include "fio.h"
-#include "os.h"
+
+#include "../fio.h"
+#include "../os.h"
 
 struct mmapio_data {
 	struct io_u *last_io_u;
@@ -88,7 +89,7 @@
 	return 0;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "mmap",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_mmapio_init,
@@ -98,3 +99,13 @@
 	.cleanup	= fio_mmapio_cleanup,
 	.flags		= FIO_SYNCIO | FIO_MMAPIO,
 };
+
+static void fio_init fio_mmapio_register(void)
+{
+	register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_mmapio_unregister(void)
+{
+	unregister_ioengine(&ioengine);
+}
diff --git a/engines/fio-engine-posixaio.c b/engines/fio-engine-posixaio.c
index 894a410..ef4d78e 100644
--- a/engines/fio-engine-posixaio.c
+++ b/engines/fio-engine-posixaio.c
@@ -7,8 +7,9 @@
 #include <unistd.h>
 #include <errno.h>
 #include <assert.h>
-#include "fio.h"
-#include "os.h"
+
+#include "../fio.h"
+#include "../os.h"
 
 #ifdef FIO_HAVE_POSIXAIO
 
@@ -179,7 +180,7 @@
 	return 0;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "posixaio",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_posixaio_init,
@@ -204,10 +205,20 @@
 	return 1;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "posixaio",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_posixaio_init,
 };
 
 #endif
+
+static void fio_init fio_posixaio_register(void)
+{
+	register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_posixaio_unregister(void)
+{
+	unregister_ioengine(&ioengine);
+}
diff --git a/engines/fio-engine-sg.c b/engines/fio-engine-sg.c
index 2762e0b..9c5037f 100644
--- a/engines/fio-engine-sg.c
+++ b/engines/fio-engine-sg.c
@@ -8,8 +8,9 @@
 #include <errno.h>
 #include <assert.h>
 #include <sys/poll.h>
-#include "fio.h"
-#include "os.h"
+
+#include "../fio.h"
+#include "../os.h"
 
 #ifdef FIO_HAVE_SGIO
 
@@ -313,7 +314,7 @@
 	return 1;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "sg",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_sgio_init,
@@ -338,10 +339,20 @@
 	return 1;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "sgio",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_sgio_init,
 };
 
 #endif
+
+static void fio_init fio_sgio_register(void)
+{
+	register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_sgio_unregister(void)
+{
+	unregister_ioengine(&ioengine);
+}
diff --git a/engines/fio-engine-splice.c b/engines/fio-engine-splice.c
index 3b02fbf..fa4a6ee 100644
--- a/engines/fio-engine-splice.c
+++ b/engines/fio-engine-splice.c
@@ -8,8 +8,9 @@
 #include <errno.h>
 #include <assert.h>
 #include <sys/poll.h>
-#include "fio.h"
-#include "os.h"
+
+#include "../fio.h"
+#include "../os.h"
 
 #ifdef FIO_HAVE_SPLICE
 
@@ -181,7 +182,7 @@
 	return 0;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "splice",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_spliceio_init,
@@ -205,10 +206,20 @@
 	return 1;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "splice",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_spliceio_init,
 };
 
 #endif
+
+static void fio_init fio_spliceio_register(void)
+{
+	register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_spliceio_unregister(void)
+{
+	unregister_ioengine(&ioengine);
+}
diff --git a/engines/fio-engine-sync.c b/engines/fio-engine-sync.c
index 43f42ca..5919830 100644
--- a/engines/fio-engine-sync.c
+++ b/engines/fio-engine-sync.c
@@ -7,8 +7,9 @@
 #include <unistd.h>
 #include <errno.h>
 #include <assert.h>
-#include "fio.h"
-#include "os.h"
+
+#include "../fio.h"
+#include "../os.h"
 
 struct syncio_data {
 	struct io_u *last_io_u;
@@ -97,7 +98,7 @@
 	return 0;
 }
 
-struct ioengine_ops ioengine = {
+static struct ioengine_ops ioengine = {
 	.name		= "sync",
 	.version	= FIO_IOOPS_VERSION,
 	.init		= fio_syncio_init,
@@ -108,3 +109,13 @@
 	.cleanup	= fio_syncio_cleanup,
 	.flags		= FIO_SYNCIO,
 };
+
+static void fio_init fio_syncio_register(void)
+{
+	register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_syncio_unregister(void)
+{
+	unregister_ioengine(&ioengine);
+}
diff --git a/fio.h b/fio.h
index f859608..9f5201b 100644
--- a/fio.h
+++ b/fio.h
@@ -565,6 +565,7 @@
 	} while (0)
 
 struct ioengine_ops {
+	struct list_head list;
 	char name[16];
 	int version;
 	int flags;
@@ -583,12 +584,16 @@
 #define FIO_IOOPS_VERSION	3
 
 extern struct ioengine_ops *load_ioengine(struct thread_data *, const char *);
+extern int register_ioengine(struct ioengine_ops *);
+extern void unregister_ioengine(struct ioengine_ops *);
 extern void close_ioengine(struct thread_data *);
 
 /*
  * Mark unused variables passed to ops functions as unused, to silence gcc
  */
 #define fio_unused	__attribute((__unused__))
+#define fio_init	__attribute__((constructor))
+#define fio_exit	__attribute__((destructor))
 
 #define for_each_td(td, i)	\
 	for ((i) = 0, (td) = &threads[0]; (i) < (int) thread_number; (i)++, (td)++)
diff --git a/init.c b/init.c
index 3749c96..00f9576 100644
--- a/init.c
+++ b/init.c
@@ -849,6 +849,7 @@
 		return 0;
 
 	log_err("fio: ioengine: { linuxaio, aio, libaio }, posixaio, sync, mmap, sgio, splice, cpu\n");
+	log_err("fio: or specify path to dynamic ioengine module\n");
 	return 1;
 }
 
diff --git a/ioengines.c b/ioengines.c
index 96a9636..a71357c 100644
--- a/ioengines.c
+++ b/ioengines.c
@@ -18,8 +18,15 @@
 #include "fio.h"
 #include "os.h"
 
+static LIST_HEAD(engine_list);
+
 static int check_engine_ops(struct ioengine_ops *ops)
 {
+	if (ops->version != FIO_IOOPS_VERSION) {
+		log_err("bad ioops version %d (want %d)\n", ops->version, FIO_IOOPS_VERSION);
+		return 1;
+	}
+
 	/*
 	 * cpu thread doesn't need to provide anything
 	 */
@@ -42,21 +49,48 @@
 	return 0;
 }
 
-struct ioengine_ops *load_ioengine(struct thread_data *td, const char *name)
+void unregister_ioengine(struct ioengine_ops *ops)
 {
-	char engine[16], engine_lib[256];
-	struct ioengine_ops *ops, *ret;
-	void *dlhandle;
+	list_del(&ops->list);
+	INIT_LIST_HEAD(&ops->list);
+}
+
+int register_ioengine(struct ioengine_ops *ops)
+{
+	if (check_engine_ops(ops))
+		return 1;
+
+	INIT_LIST_HEAD(&ops->list);
+	list_add_tail(&ops->list, &engine_list);
+	return 0;
+}
+
+static struct ioengine_ops *find_ioengine(const char *name)
+{
+	struct ioengine_ops *ops;
+	struct list_head *entry;
+	char engine[16];
 
 	strncpy(engine, name, sizeof(engine) - 1);
 
-	/*
-	 * linux libaio has alias names, so convert to what we want
-	 */
 	if (!strncmp(engine, "linuxaio", 8) || !strncmp(engine, "aio", 3))
 		strcpy(engine, "libaio");
 
-	sprintf(engine_lib, "%s/lib/fio/fio-engine-%s.o", fio_inst_prefix, engine);
+	list_for_each(entry, &engine_list) {
+		ops = list_entry(entry, struct ioengine_ops, list);
+		if (!strcmp(engine, ops->name))
+			return ops;
+	}
+
+	return NULL;
+}
+
+static struct ioengine_ops *dlopen_ioengine(struct thread_data *td,
+					    const char *engine_lib)
+{
+	struct ioengine_ops *ops;
+	void *dlhandle;
+
 	dlerror();
 	dlhandle = dlopen(engine_lib, RTLD_LAZY);
 	if (!dlhandle) {
@@ -71,24 +105,41 @@
 		return NULL;
 	}
 
-	if (ops->version != FIO_IOOPS_VERSION) {
-		log_err("bad ioops version %d (want %d)\n", ops->version, FIO_IOOPS_VERSION);
-		dlclose(dlhandle);
+	ops->dlhandle = dlhandle;
+	return ops;
+}
+
+struct ioengine_ops *load_ioengine(struct thread_data *td, const char *name)
+{
+	struct ioengine_ops *ops, *ret;
+	char engine[16];
+
+	strncpy(engine, name, sizeof(engine) - 1);
+
+	/*
+	 * linux libaio has alias names, so convert to what we want
+	 */
+	if (!strncmp(engine, "linuxaio", 8) || !strncmp(engine, "aio", 3))
+		strcpy(engine, "libaio");
+
+	ops = find_ioengine(engine);
+	if (!ops)
+		ops = dlopen_ioengine(td, name);
+
+	if (!ops) {
+		log_err("fio: engine %s not loadable\n", name);
 		return NULL;
 	}
 
 	/*
 	 * Check that the required methods are there.
 	 */
-	if (check_engine_ops(ops)) {
-		dlclose(dlhandle);
+	if (check_engine_ops(ops))
 		return NULL;
-	}
 
 	ret = malloc(sizeof(*ret));
 	memcpy(ret, ops, sizeof(*ret));
 	ret->data = NULL;
-	ret->dlhandle = dlhandle;
 
 	return ret;
 }
@@ -98,7 +149,9 @@
 	if (td->io_ops->cleanup)
 		td->io_ops->cleanup(td);
 
-	dlclose(td->io_ops->dlhandle);
+	if (td->io_ops->dlhandle)
+		dlclose(td->io_ops->dlhandle);
+
 	free(td->io_ops);
 	td->io_ops = NULL;
 }
diff --git a/list.h b/list.h
index 2e0a7ad..cedbafa 100644
--- a/list.h
+++ b/list.h
@@ -28,6 +28,9 @@
 
 #define LIST_HEAD_INIT(name) { &(name), &(name) }
 
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
 #define INIT_LIST_HEAD(ptr) do { \
 	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
 } while (0)