Add option to select how to service multiple files

Right now we just round robin the open files, but sometimes you
just want to randomly go through the files. Add a 'file_service'
option for that, default to round robin still.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/fio.h b/fio.h
index 959e52f..8c46b3f 100644
--- a/fio.h
+++ b/fio.h
@@ -280,7 +280,10 @@
 	struct fio_file *files;
 	unsigned int nr_files;
 	unsigned int nr_uniq_files;
-	unsigned int next_file;
+	union {
+		unsigned int next_file;
+		os_random_state_t next_file_state;
+	};
 	int error;
 	pid_t pid;
 	char *orig_buffer;
@@ -340,6 +343,7 @@
 	unsigned int rwmixread;
 	unsigned int rwmixwrite;
 	unsigned int nice;
+	unsigned int file_service_type;
 
 	char *read_iolog_file;
 	char *write_iolog_file;
@@ -437,6 +441,14 @@
 };
 
 /*
+ * roundrobin available files, or choose one at random.
+ */
+enum {
+	FIO_FSERVICE_RANDOM	= 1,
+	FIO_FSERVICE_RR		= 2,
+};
+
+/*
  * 30 second per-io_u timeout, with 5 second intervals to avoid resetting
  * the timer on each queue operation.
  */
diff --git a/init.c b/init.c
index c8f27ad..01e615f 100644
--- a/init.c
+++ b/init.c
@@ -33,6 +33,7 @@
 #endif
 static int str_exitall_cb(void);
 static int str_cpumask_cb(void *, unsigned int *);
+static int str_file_service_cb(void *, const char *);
 
 #define __stringify_1(x)	#x
 #define __stringify(x)		__stringify_1(x)
@@ -153,6 +154,14 @@
 		.def	= "1",
 	},
 	{
+		.name	= "file_service_type",
+		.type	= FIO_OPT_STR,
+		.cb	= str_file_service_cb,
+		.help	= "How to select which file to service next",
+		.def	= "roundrobin",
+		.posval	= { "random", "roundrobin" },
+	},
+	{
 		.name	= "fsync",
 		.type	= FIO_OPT_INT,
 		.off1	= td_var_offset(fsync_blocks),
@@ -841,7 +850,7 @@
  */
 int init_random_state(struct thread_data *td)
 {
-	unsigned long seeds[4];
+	unsigned long seeds[5];
 	int fd, num_maps, blocks, i;
 	struct fio_file *f;
 
@@ -865,12 +874,13 @@
 	os_random_seed(seeds[0], &td->bsrange_state);
 	os_random_seed(seeds[1], &td->verify_state);
 	os_random_seed(seeds[2], &td->rwmix_state);
+	os_random_seed(seeds[3], &td->next_file_state);
 
 	if (td->sequential)
 		return 0;
 
 	if (td->rand_repeatable)
-		seeds[3] = FIO_RANDSEED * td->thread_number;
+		seeds[4] = FIO_RANDSEED * td->thread_number;
 
 	if (!td->norandommap) {
 		for_each_file(td, f, i) {
@@ -882,7 +892,7 @@
 		}
 	}
 
-	os_random_seed(seeds[3], &td->random_state);
+	os_random_seed(seeds[4], &td->random_state);
 	return 0;
 }
 
@@ -1087,6 +1097,22 @@
 	return 0;
 }
 
+static int str_file_service_cb(void *data, const char *str)
+{
+	struct thread_data *td = data;
+
+	if (!strncmp(str, "random", 6)) {
+		td->file_service_type = FIO_FSERVICE_RANDOM;
+		return 0;
+	} else if (!strncmp(str, "roundrobin", 10)) {
+		td->file_service_type = FIO_FSERVICE_RR;
+		return 0;
+	}
+
+	log_err("fio: file_service= random, roundrobin\n");
+	return 1;
+}
+
 /*
  * This is our [ini] type file parser.
  */
diff --git a/io_u.c b/io_u.c
index ab85193..6fb754e 100644
--- a/io_u.c
+++ b/io_u.c
@@ -325,7 +325,27 @@
 	td->io_u_lat[index]++;
 }
 
-static struct fio_file *get_next_file(struct thread_data *td)
+/*
+ * Get next file to service by choosing one at random
+ */
+static struct fio_file *get_next_file_rand(struct thread_data *td)
+{
+	long r = os_random_long(&td->next_file_state);
+	unsigned int fileno;
+	struct fio_file *f;
+
+	do {
+		fileno = (unsigned int) ((double) (td->nr_files - 1) * r / (RAND_MAX + 1.0));
+		f = &td->files[fileno];
+		if (f->fd != -1)
+			return f;
+	} while (1);
+}
+
+/*
+ * Get next file to service by doing round robin between all available ones
+ */
+static struct fio_file *get_next_file_rr(struct thread_data *td)
 {
 	unsigned int old_next_file = td->next_file;
 	struct fio_file *f;
@@ -393,7 +413,11 @@
 	if (io_u->file)
 		goto out;
 
-	f = get_next_file(td);
+	if (td->file_service_type == FIO_FSERVICE_RR)
+		f = get_next_file_rr(td);
+	else
+		f = get_next_file_rand(td);
+
 	if (!f) {
 		put_io_u(td, io_u);
 		return NULL;