Add support for loadable profiles

Split the only existing profile, tiobench, into this setup.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/Makefile b/Makefile
index ce63cfc..ce2374b 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@
 OBJS = gettime.o fio.o ioengines.o init.o stat.o log.o time.o filesetup.o \
 	eta.o verify.o memory.o io_u.o parse.o mutex.o options.o \
 	rbtree.o diskutil.o fifo.o blktrace.o smalloc.o filehash.o helpers.o \
-	cgroup.o
+	cgroup.o profile.o
 
 OBJS += crc/crc7.o
 OBJS += crc/crc16.o
@@ -32,6 +32,8 @@
 OBJS += engines/syslet-rw.o
 OBJS += engines/guasi.o
 
+OBJS += profiles/tiobench.o
+
 ifneq ($(findstring $(MAKEFLAGS),s),s)
 ifndef V
 	QUIET_CC	= @echo '   ' CC $@;
diff --git a/debug.h b/debug.h
index 1a2b79a..71b346d 100644
--- a/debug.h
+++ b/debug.h
@@ -16,6 +16,7 @@
 	FD_DISKUTIL,
 	FD_JOB,
 	FD_MUTEX,
+	FD_PROFILE,
 	FD_DEBUG_MAX,
 };
 
diff --git a/fio.h b/fio.h
index bdc1708..7eb02ad 100644
--- a/fio.h
+++ b/fio.h
@@ -28,7 +28,6 @@
 #include "io_ddir.h"
 #include "ioengine.h"
 #include "iolog.h"
-#include "profiles.h"
 #include "helpers.h"
 
 #ifdef FIO_HAVE_GUASI
@@ -271,7 +270,7 @@
 	/*
 	 * Benchmark profile type
 	 */
-	unsigned int profile;
+	char *profile;
 
 	/*
 	 * blkio cgroup support
@@ -538,6 +537,7 @@
 extern void options_mem_dupe(struct thread_data *);
 extern void options_mem_free(struct thread_data *);
 extern void td_fill_rand_seeds(struct thread_data *);
+extern void add_job_opts(const char **);
 #define FIO_GETOPT_JOB		0x89988998
 #define FIO_NR_OPTIONS		128
 
diff --git a/flist.h b/flist.h
index 95023c7..7aca973 100644
--- a/flist.h
+++ b/flist.h
@@ -1,6 +1,8 @@
 #ifndef _LINUX_FLIST_H
 #define _LINUX_FLIST_H
 
+#include <stdlib.h>
+
 #undef offsetof
 #ifdef __compiler_offsetof
 #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
diff --git a/init.c b/init.c
index 7785e9e..c0ce431 100644
--- a/init.c
+++ b/init.c
@@ -19,6 +19,7 @@
 #include "smalloc.h"
 #include "filehash.h"
 #include "verify.h"
+#include "profile.h"
 
 static char fio_version_string[] = "fio 1.37";
 
@@ -136,26 +137,6 @@
 	},
 };
 
-static const char *tiobench_opts[] = {
-	"buffered=0", "size=4*1024*$mb_memory", "bs=4k", "timeout=600",
-	"numjobs=4", "group_reporting", "thread", "overwrite=1",
-	"filename=.fio.tio.1:.fio.tio.2:.fio.tio.3:.fio.tio.4",
-	"name=seqwrite", "rw=write", "end_fsync=1",
-	"name=randwrite", "stonewall", "rw=randwrite", "end_fsync=1",
-	"name=seqread", "stonewall", "rw=read",
-	"name=randread", "stonewall", "rw=randread", NULL,
-};
-
-static const char **fio_prof_strings[PROFILE_END] = {
-	NULL,
-	tiobench_opts,
-};
-
-static const char *profiles[PROFILE_END] = {
-	"none",
-	"tiobench",
-};
-
 FILE *get_f_out()
 {
 	return f_out;
@@ -655,6 +636,43 @@
 	return -1;
 }
 
+/*
+ * Parse as if 'o' was a command line
+ */
+void add_job_opts(const char **o)
+{
+	struct thread_data *td, *td_parent;
+	int i, in_global = 1;
+	char jobname[32];
+
+	i = 0;
+	td_parent = td = NULL;
+	while (o[i]) {
+		if (!strncmp(o[i], "name", 4)) {
+			in_global = 0;
+			if (td)
+				add_job(td, jobname, 0);
+			td = NULL;
+			sprintf(jobname, "%s", o[i] + 5);
+		}
+		if (in_global && !td_parent)
+			td_parent = get_new_job(1, &def_thread);
+		else if (!in_global && !td) {
+			if (!td_parent)
+				td_parent = &def_thread;
+			td = get_new_job(0, td_parent);
+		}
+		if (in_global)
+			fio_options_parse(td_parent, (char **) &o[i], 1);
+		else
+			fio_options_parse(td, (char **) &o[i], 1);
+		i++;
+	}
+
+	if (td)
+		add_job(td, jobname, 0);
+}
+
 static int skip_this_section(const char *name)
 {
 	if (!job_section)
@@ -961,6 +979,7 @@
 	{ .name = "diskutil",	.shift = FD_DISKUTIL },
 	{ .name = "job",	.shift = FD_JOB },
 	{ .name = "mutex",	.shift = FD_MUTEX },
+	{ .name	= "profile",	.shift = FD_PROFILE },
 	{ .name = NULL, },
 };
 
@@ -1028,57 +1047,6 @@
 }
 #endif
 
-static int load_profile(const char *profile)
-{
-	struct thread_data *td, *td_parent;
-	const char **o;
-	int i, in_global = 1;
-	char jobname[32];
-
-	dprint(FD_PARSE, "loading profile '%s'\n", profile);
-
-	for (i = 0; i < PROFILE_END; i++) {
-		if (!strcmp(profile, profiles[i]))
-			break;
-	}
-
-	if (i == PROFILE_END) {
-		log_err("fio: unknown profile '%s'\n", profile);
-		return 1;
-	}
-
-	o = fio_prof_strings[i];
-	if (!o)
-		return 0;
-
-	i = 0;
-	td_parent = td = NULL;
-	while (o[i]) {
-		if (!strncmp(o[i], "name", 4)) {
-			in_global = 0;
-			if (td)
-				add_job(td, jobname, 0);
-			td = NULL;
-			sprintf(jobname, "%s", o[i] + 5);
-		}
-		if (in_global && !td_parent)
-			td_parent = get_new_job(1, &def_thread);
-		else if (!in_global && !td) {
-			if (!td_parent)
-				td_parent = &def_thread;
-			td = get_new_job(0, td_parent);
-		}
-		if (in_global)
-			fio_options_parse(td_parent, (char **) &o[i], 1);
-		else
-			fio_options_parse(td, (char **) &o[i], 1);
-		i++;
-	}
-	if (td)
-		add_job(td, jobname, 0);
-	return 0;
-}
-
 static int parse_cmd_line(int argc, char *argv[])
 {
 	struct thread_data *td = NULL;
diff --git a/options.c b/options.c
index 0e57e52..03b2437 100644
--- a/options.c
+++ b/options.c
@@ -1726,14 +1726,8 @@
 	},
 	{
 		.name	= "profile",
-		.type	= FIO_OPT_STR,
+		.type	= FIO_OPT_STR_STORE,
 		.off1	= td_var_offset(profile),
-		.posval = {
-			  { .ival = "tiobench",
-			    .oval = PROFILE_TIOBENCH,
-			    .help = "Perform tiobench like test",
-			  },
-		},
 		.help	= "Select a specific builtin performance test",
 	},
 	{
diff --git a/profile.c b/profile.c
new file mode 100644
index 0000000..e56ca29
--- /dev/null
+++ b/profile.c
@@ -0,0 +1,42 @@
+#include "fio.h"
+#include "profile.h"
+#include "debug.h"
+#include "flist.h"
+
+static FLIST_HEAD(profile_list);
+
+int load_profile(const char *profile)
+{
+	struct profile_ops *ops;
+	struct flist_head *n;
+
+	dprint(FD_PROFILE, "loading profile '%s'\n", profile);
+
+	flist_for_each(n, &profile_list) {
+		ops = flist_entry(n, struct profile_ops, list);
+		if (!strcmp(profile, ops->name))
+			break;
+
+		ops = NULL;
+	}
+
+	if (ops) {
+		add_job_opts(ops->def_ops);
+		return 0;
+	}
+
+	log_err("fio: profile '%s' not found\n", profile);
+	return 1;
+}
+
+void register_profile(struct profile_ops *ops)
+{
+	dprint(FD_PROFILE, "register profile '%s'\n", ops->name);
+	flist_add_tail(&ops->list, &profile_list);
+}
+
+void unregister_profile(struct profile_ops *ops)
+{
+	dprint(FD_PROFILE, "unregister profile '%s'\n", ops->name);
+	flist_del(&ops->list);
+}
diff --git a/profile.h b/profile.h
new file mode 100644
index 0000000..6b19606
--- /dev/null
+++ b/profile.h
@@ -0,0 +1,21 @@
+#ifndef FIO_PROFILE_H
+#define FIO_PROFILE_H
+
+#include "flist.h"
+
+#define FIO_PROFILE_VERSION	1
+
+struct profile_ops {
+	struct flist_head list;
+	char name[32];
+	int version;
+	int flags;
+
+	const char **def_ops;
+};
+
+void register_profile(struct profile_ops *);
+void unregister_profile(struct profile_ops *);
+int load_profile(const char *);
+
+#endif
diff --git a/profiles.h b/profiles.h
deleted file mode 100644
index 8203a46..0000000
--- a/profiles.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef FIO_PROFILE_H
-#define FIO_PROFILE_H
-
-enum {
-	PROFILE_NONE		= 0,
-	PROFILE_TIOBENCH	= 1,
-	PROFILE_END		= 2,
-};
-
-#endif
diff --git a/profiles/tiobench.c b/profiles/tiobench.c
new file mode 100644
index 0000000..d239468
--- /dev/null
+++ b/profiles/tiobench.c
@@ -0,0 +1,28 @@
+#include "../fio.h"
+#include "../profile.h"
+
+static const char *tb_opts[] = {
+	"buffered=0", "size=4*1024*$mb_memory", "bs=4k", "timeout=600",
+	"numjobs=4", "group_reporting", "thread", "overwrite=1",
+	"filename=.fio.tio.1:.fio.tio.2:.fio.tio.3:.fio.tio.4",
+	"name=seqwrite", "rw=write", "end_fsync=1",
+	"name=randwrite", "stonewall", "rw=randwrite", "end_fsync=1",
+	"name=seqread", "stonewall", "rw=read",
+	"name=randread", "stonewall", "rw=randread", NULL,
+};
+
+static struct profile_ops tiobench_profile = {
+	.name		= "tiobench",
+	.version	= FIO_PROFILE_VERSION,
+	.def_ops	= tb_opts,
+};
+
+static void fio_init tiobench_register(void)
+{
+	register_profile(&tiobench_profile);
+}
+
+static void fio_exit tiobench_unregister(void)
+{
+	unregister_profile(&tiobench_profile);
+}