Add sub-option support (sort-of) and convert libaio_userspace_reap

You should now use ioengine=libaio:userspace_reap instead of
the separate option.

Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
diff --git a/HOWTO b/HOWTO
index ad4e454..724b604 100644
--- a/HOWTO
+++ b/HOWTO
@@ -517,6 +517,16 @@
 			libaio	Linux native asynchronous io. Note that Linux
 				may only support queued behaviour with
 				non-buffered IO (set direct=1 or buffered=0).
+				This engine also has a sub-option,
+				userspace_reap. To set it, use
+				ioengine=libaio:userspace_reap. Normally, with
+				the libaio engine in use, fio will use the
+				io_getevents system call to reap newly returned
+				events. With this flag turned on, the AIO ring
+				will be read directly from user-space to reap
+				events. The reaping mode is only enabled when
+				polling for a minimum of 0 events (eg when
+				iodepth_batch_complete=0).
 
 			posixaio glibc posix asynchronous io.
 
@@ -1187,13 +1197,6 @@
 
 gid=int		Set group ID, see uid.
 
-userspace_libaio_reap=bool  Normally, with the libaio engine in use, fio
-		will use the io_getevents system call to reap newly returned
-		events. With this flag turned on, the AIO ring will be read
-		directly from user-space to reap events. The reaping mode is
-		only enabled when polling for a minimum of 0 events (eg when
-		iodepth_batch_complete=0).
-
 6.0 Interpreting the output
 ---------------------------
 
diff --git a/engines/libaio.c b/engines/libaio.c
index ea05c63..ad34d06 100644
--- a/engines/libaio.c
+++ b/engines/libaio.c
@@ -63,7 +63,7 @@
 	unsigned nr;		 /** number of io_events */
 	unsigned head;
 	unsigned tail;
- 
+
 	unsigned magic;
 	unsigned compat_features;
 	unsigned incompat_features;
@@ -75,11 +75,11 @@
 #define AIO_RING_MAGIC	0xa10a10a1
 
 static int user_io_getevents(io_context_t aio_ctx, unsigned int max,
-			struct io_event *events)
+			     struct io_event *events)
 {
 	long i = 0;
 	unsigned head;
-	struct aio_ring *ring = (struct aio_ring*)aio_ctx;
+	struct aio_ring *ring = (struct aio_ring*) aio_ctx;
 
 	while (i < max) {
 		head = ring->head;
@@ -91,7 +91,7 @@
 			/* There is another completion to reap */
 			events[i] = ring->events[head];
 			read_barrier();
-    			ring->head = (head + 1) % ring->nr;
+			ring->head = (head + 1) % ring->nr;
 			i++;
 		}
 	}
diff --git a/fio.1 b/fio.1
index 488896c..ffc97c9 100644
--- a/fio.1
+++ b/fio.1
@@ -367,7 +367,13 @@
 coalescing adjacents IOs into a single submission.
 .TP
 .B libaio
-Linux native asynchronous I/O.
+Linux native asynchronous I/O.  This engine also has a sub-option,
+\fBuserspace_reap\fR. To set it, use \fBioengine=libaio:userspace_reap\fR.
+Normally, with the libaio engine in use, fio will use the
+\fIio_getevents\fR\|(3) system call to reap newly returned events. With this
+flag turned on, the AIO ring will be read directly from user-space to reap
+events. The reaping mode is only enabled when polling for a minimum of \fB0\fR
+events (eg when \fBiodepth_batch_complete=0\fR).
 .TP
 .B posixaio
 POSIX asynchronous I/O using \fIaio_read\fR\|(3) and \fIaio_write\fR\|(3).
diff --git a/options.c b/options.c
index 6f7c41e..74c24d0 100644
--- a/options.c
+++ b/options.c
@@ -226,6 +226,21 @@
 	return 0;
 }
 
+#ifdef FIO_HAVE_LIBAIO
+static int str_libaio_cb(void *data, const char *str)
+{
+	struct thread_data *td = data;
+
+	if (!strcmp(str, "userspace_reap")) {
+		td->o.userspace_libaio_reap = 1;
+		return 0;
+	}
+
+	log_err("fio: bad libaio sub-option: %s\n", str);
+	return 1;
+}
+#endif
+
 static int str_mem_cb(void *data, const char *mem)
 {
 	struct thread_data *td = data;
@@ -961,6 +976,7 @@
 #ifdef FIO_HAVE_LIBAIO
 			  { .ival = "libaio",
 			    .help = "Linux native asynchronous IO",
+			    .cb   = str_libaio_cb,
 			  },
 #endif
 #ifdef FIO_HAVE_POSIXAIO
@@ -2069,15 +2085,6 @@
 		.off1	= td_var_offset(gid),
 		.help	= "Run job with this group ID",
 	},
-#ifdef FIO_HAVE_LIBAIO
-	{
-		.name	= "userspace_libaio_reap",
-		.type	= FIO_OPT_BOOL,
-		.off1	= td_var_offset(userspace_libaio_reap),
-		.help	= "When using the libaio engine with iodepth_batch_complete=0, enable userspace reaping",
-		.def	= "0",
-	},
-#endif
 	{
 		.name = NULL,
 	},
diff --git a/parse.c b/parse.c
index c2c5bf2..4458121 100644
--- a/parse.c
+++ b/parse.c
@@ -346,6 +346,9 @@
 	double uf;
 	char **cp;
 	int ret = 0, is_time = 0;
+	const struct value_pair *vp;
+	struct value_pair posval[PARSE_MAX_VP];
+	int i, all_skipped = 1;
 
 	dprint(FD_PARSE, "__handle_option=%s, type=%d, ptr=%s\n", o->name,
 							o->type, ptr);
@@ -359,9 +362,6 @@
 	case FIO_OPT_STR:
 	case FIO_OPT_STR_MULTI: {
 		fio_opt_str_fn *fn = o->cb;
-		const struct value_pair *vp;
-		struct value_pair posval[PARSE_MAX_VP];
-		int i, all_skipped = 1;
 
 		posval_sort(o, posval);
 
@@ -487,19 +487,40 @@
 	case FIO_OPT_STR_STORE: {
 		fio_opt_str_fn *fn = o->cb;
 
-		if (o->roff1)
-			cp = (char **) o->roff1;
-		else
-			cp = td_var(data, o->off1);
+		posval_sort(o, posval);
 
-		*cp = strdup(ptr);
-		if (fn) {
-			ret = fn(data, ptr);
-			if (ret) {
-				free(*cp);
-				*cp = NULL;
+		ret = 1;
+		for (i = 0; i < PARSE_MAX_VP; i++) {
+			vp = &posval[i];
+			if (!vp->ival || vp->ival[0] == '\0')
+				continue;
+			all_skipped = 0;
+			if (!strncmp(vp->ival, ptr, opt_len(ptr))) {
+				char *rest;
+
+				ret = 0;
+				if (vp->cb)
+					fn = vp->cb;
+				if (o->roff1)
+					cp = (char **) o->roff1;
+				else
+					cp = td_var(data, o->off1);
+				*cp = strdup(ptr);
+				rest = strstr(*cp, ":");
+				if (rest) {
+					*rest = '\0';
+					ptr = rest + 1;
+				} else
+					ptr = NULL;
+				break;
 			}
 		}
+
+		if (ret && !all_skipped)
+			show_option_values(o);
+		else if (fn && ptr)
+			ret = fn(data, ptr);
+
 		break;
 	}
 	case FIO_OPT_RANGE: {
diff --git a/parse.h b/parse.h
index c5a7417..f2265a4 100644
--- a/parse.h
+++ b/parse.h
@@ -29,6 +29,7 @@
 	unsigned int oval;		/* output value */
 	const char *help;		/* help text for sub option */
 	int or;				/* OR value */
+	void *cb;			/* sub-option callback */
 };
 
 #define OPT_LEN_MAX 	4096