Add option for specifically setting buffer contents

Fio can use zeroes, slightly scrambled data, full random data,
or specifically compressible data. With this option, the user
can now fully control the pattern written, similarly to how
verify_pattern works for verify=meta.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/HOWTO b/HOWTO
index 2183be1..c3672d8 100644
--- a/HOWTO
+++ b/HOWTO
@@ -547,6 +547,11 @@
 		alternate random and zeroed data throughout the IO
 		buffer.
 
+buffer_pattern=str	If set, fio will fill the io buffers with this pattern.
+		If not set, the contents of io buffers is defined by the other
+		options related to buffer contents. The setting can be any
+		pattern of bytes, and can be prefixed with 0x for hex values.
+
 nrfiles=int	Number of files to use for this job. Defaults to 1.
 
 openfiles=int	Number of files to keep open at the same time. Defaults to
diff --git a/backend.c b/backend.c
index 101024d..c9a20a3 100644
--- a/backend.c
+++ b/backend.c
@@ -989,7 +989,7 @@
 				 * Fill the buffer with the pattern if we are
 				 * going to be doing writes.
 				 */
-				fill_pattern(td, io_u->buf, max_bs, io_u, 0, 0);
+				fill_verify_pattern(td, io_u->buf, max_bs, io_u, 0, 0);
 			}
 		}
 
diff --git a/fio.1 b/fio.1
index 6c3dd21..82d05b7 100644
--- a/fio.1
+++ b/fio.1
@@ -426,6 +426,12 @@
 the remaining zeroed. With this set to some chunk size smaller than the block
 size, fio can alternate random and zeroed data throughout the IO buffer.
 .TP
+.BI buffer_pattern \fR=\fPstr
+If set, fio will fill the io buffers with this pattern. If not set, the contents
+of io buffers is defined by the other options related to buffer contents. The
+setting can be any pattern of bytes, and can be prefixed with 0x for hex
+values.
+.TP
 .BI nrfiles \fR=\fPint
 Number of files to use for this job.  Default: 1.
 .TP
diff --git a/io_u.c b/io_u.c
index ea6c251..518d884 100644
--- a/io_u.c
+++ b/io_u.c
@@ -1770,7 +1770,9 @@
 void fill_io_buffer(struct thread_data *td, void *buf, unsigned int min_write,
 		    unsigned int max_bs)
 {
-	if (!td->o.zero_buffers) {
+	if (td->o.buffer_pattern_bytes)
+		fill_buffer_pattern(td, buf, max_bs);
+	else if (!td->o.zero_buffers) {
 		unsigned int perc = td->o.compress_percentage;
 
 		if (perc) {
diff --git a/options.c b/options.c
index 16b6636..b1b6c8e 100644
--- a/options.c
+++ b/options.c
@@ -834,11 +834,12 @@
 	return add_dir_files(td, td->o.opendir);
 }
 
-static int str_verify_pattern_cb(void *data, const char *input)
+static int pattern_cb(char *pattern, unsigned int max_size,
+		      const char *input, unsigned int *pattern_bytes)
 {
-	struct thread_data *td = data;
 	long off;
-	int i = 0, j = 0, len, k, base = 10, pattern_length;
+	int i = 0, j = 0, len, k, base = 10;
+	uint32_t pattern_length;
 	char *loc1, *loc2;
 
 	loc1 = strstr(input, "0x");
@@ -848,7 +849,7 @@
 	off = strtol(input, NULL, base);
 	if (off != LONG_MAX || errno != ERANGE) {
 		while (off) {
-			td->o.verify_pattern[i] = off & 0xff;
+			pattern[i] = off & 0xff;
 			off >>= 8;
 			i++;
 		}
@@ -862,13 +863,13 @@
 				j = loc2 - input + 2;
 		} else
 			return 1;
-		if (len - j < MAX_PATTERN_SIZE * 2) {
+		if (len - j < max_size * 2) {
 			while (k >= j) {
 				off = converthexchartoint(input[k--]);
 				if (k >= j)
 					off += (converthexchartoint(input[k--])
 						* 16);
-				td->o.verify_pattern[i++] = (char) off;
+				pattern[i++] = (char) off;
 			}
 		}
 	}
@@ -878,19 +879,19 @@
 	 * the number of memcpy's we have to do when verifying the IO.
 	 */
 	pattern_length = i;
-	while (i > 1 && i * 2 <= MAX_PATTERN_SIZE) {
-		memcpy(&td->o.verify_pattern[i], &td->o.verify_pattern[0], i);
+	while (i > 1 && i * 2 <= max_size) {
+		memcpy(&pattern[i], &pattern[0], i);
 		i *= 2;
 	}
 
 	/*
 	 * Fill remainder, if the pattern multiple ends up not being
-	 * MAX_PATTERN_SIZE.
+	 * max_size.
 	 */
-	while (i > 1 && i < MAX_PATTERN_SIZE) {
-		unsigned int b = min(pattern_length, MAX_PATTERN_SIZE - i);
+	while (i > 1 && i < max_size) {
+		unsigned int b = min(pattern_length, max_size - i);
 
-		memcpy(&td->o.verify_pattern[i], &td->o.verify_pattern[0], b);
+		memcpy(&pattern[i], &pattern[0], b);
 		i += b;
 	}
 
@@ -899,19 +900,45 @@
 		 * The code in verify_io_u_pattern assumes a single byte pattern
 		 * fills the whole verify pattern buffer.
 		 */
-		memset(td->o.verify_pattern, td->o.verify_pattern[0],
-		       MAX_PATTERN_SIZE);
+		memset(pattern, pattern[0], max_size);
 	}
 
-	td->o.verify_pattern_bytes = i;
+	*pattern_bytes = i;
+	return 0;
+}
+
+static int str_buffer_pattern_cb(void *data, const char *input)
+{
+	struct thread_data *td = data;
+	int ret;
+
+	ret = pattern_cb(td->o.buffer_pattern, MAX_PATTERN_SIZE, input,
+				&td->o.buffer_pattern_bytes);
+
+	if (!ret) {
+		td->o.refill_buffers = 0;
+		td->o.scramble_buffers = 0;
+		td->o.zero_buffers = 0;
+	}
+
+	return ret;
+}
+
+static int str_verify_pattern_cb(void *data, const char *input)
+{
+	struct thread_data *td = data;
+	int ret;
+
+	ret = pattern_cb(td->o.verify_pattern, MAX_PATTERN_SIZE, input,
+				&td->o.verify_pattern_bytes);
 
 	/*
 	 * VERIFY_META could already be set
 	 */
-	if (td->o.verify == VERIFY_NONE)
+	if (!ret && td->o.verify == VERIFY_NONE)
 		td->o.verify = VERIFY_PATTERN;
 
-	return 0;
+	return ret;
 }
 
 static int str_gtod_reduce_cb(void *data, int *il)
@@ -2947,6 +2974,15 @@
 		.group	= FIO_OPT_G_IO_BUF,
 	},
 	{
+		.name	= "buffer_pattern",
+		.lname	= "Buffer pattern",
+		.type	= FIO_OPT_STR,
+		.cb	= str_buffer_pattern_cb,
+		.help	= "Fill pattern for IO buffers",
+		.category = FIO_OPT_C_IO,
+		.group	= FIO_OPT_G_IO_BUF,
+	},
+	{
 		.name	= "buffer_compress_percentage",
 		.lname	= "Buffer compression percentage",
 		.type	= FIO_OPT_INT,
diff --git a/thread_options.h b/thread_options.h
index e2c6e88..f40a992 100644
--- a/thread_options.h
+++ b/thread_options.h
@@ -170,6 +170,8 @@
 	unsigned int zero_buffers;
 	unsigned int refill_buffers;
 	unsigned int scramble_buffers;
+	char buffer_pattern[MAX_PATTERN_SIZE];
+	unsigned int buffer_pattern_bytes;
 	unsigned int compress_percentage;
 	unsigned int compress_chunk;
 	unsigned int time_based;
@@ -381,6 +383,8 @@
 	uint32_t zero_buffers;
 	uint32_t refill_buffers;
 	uint32_t scramble_buffers;
+	uint8_t buffer_pattern[MAX_PATTERN_SIZE];
+	uint32_t buffer_pattern_bytes;
 	unsigned int compress_percentage;
 	unsigned int compress_chunk;
 	uint32_t time_based;
diff --git a/verify.c b/verify.c
index 0d38c0e..721aeb4 100644
--- a/verify.c
+++ b/verify.c
@@ -28,51 +28,65 @@
 			 struct verify_header *hdr, unsigned int header_num,
 			 unsigned int header_len);
 
-void fill_pattern(struct thread_data *td, void *p, unsigned int len, struct io_u *io_u, unsigned long seed, int use_seed)
+static void fill_pattern(struct thread_data *td, void *p, unsigned int len,
+			 char *pattern, unsigned int pattern_bytes)
 {
-	switch (td->o.verify_pattern_bytes) {
+	switch (pattern_bytes) {
 	case 0:
-		dprint(FD_VERIFY, "fill random bytes len=%u\n", len);
-		if (use_seed)
-			__fill_random_buf(p, len, seed);
-		else
-			io_u->rand_seed = fill_random_buf(&td->buf_state, p, len);
+		assert(0);
 		break;
 	case 1:
-		if (io_u->buf_filled_len >= len) {
-			dprint(FD_VERIFY, "using already filled verify pattern b=0 len=%u\n", len);
-			return;
-		}
 		dprint(FD_VERIFY, "fill verify pattern b=0 len=%u\n", len);
-		memset(p, td->o.verify_pattern[0], len);
-		io_u->buf_filled_len = len;
+		memset(p, pattern[0], len);
 		break;
 	default: {
 		unsigned int i = 0, size = 0;
 		unsigned char *b = p;
 
-		if (io_u->buf_filled_len >= len) {
-			dprint(FD_VERIFY, "using already filled verify pattern b=%d len=%u\n",
-					td->o.verify_pattern_bytes, len);
-			return;
-		}
-
 		dprint(FD_VERIFY, "fill verify pattern b=%d len=%u\n",
-					td->o.verify_pattern_bytes, len);
+					pattern_bytes, len);
 
 		while (i < len) {
-			size = td->o.verify_pattern_bytes;
+			size = pattern_bytes;
 			if (size > (len - i))
 				size = len - i;
-			memcpy(b+i, td->o.verify_pattern, size);
+			memcpy(b+i, pattern, size);
 			i += size;
 		}
-		io_u->buf_filled_len = len;
 		break;
 		}
 	}
 }
 
+void fill_buffer_pattern(struct thread_data *td, void *p, unsigned int len)
+{
+	fill_pattern(td, p, len, td->o.buffer_pattern, td->o.buffer_pattern_bytes);
+}
+
+void fill_verify_pattern(struct thread_data *td, void *p, unsigned int len,
+			 struct io_u *io_u, unsigned long seed, int use_seed)
+{
+	if (!td->o.verify_pattern_bytes) {
+		dprint(FD_VERIFY, "fill random bytes len=%u\n", len);
+
+		if (use_seed)
+			__fill_random_buf(p, len, seed);
+		else
+			io_u->rand_seed = fill_random_buf(&td->buf_state, p, len);
+		return;
+	}
+	
+	if (io_u->buf_filled_len >= len) {
+		dprint(FD_VERIFY, "using already filled verify pattern b=%d len=%u\n",
+			td->o.verify_pattern_bytes, len);
+		return;
+	}
+
+	fill_pattern(td, p, len, td->o.verify_pattern, td->o.verify_pattern_bytes);
+
+	io_u->buf_filled_len = len;
+}
+
 static unsigned int get_hdr_inc(struct thread_data *td, struct io_u *io_u)
 {
 	unsigned int hdr_inc;
@@ -91,7 +105,7 @@
 	struct verify_header *hdr;
 	void *p = io_u->buf;
 
-	fill_pattern(td, p, io_u->buflen, io_u, seed, use_seed);
+	fill_verify_pattern(td, p, io_u->buflen, io_u, seed, use_seed);
 
 	hdr_inc = get_hdr_inc(td, io_u);
 	header_num = 0;
diff --git a/verify.h b/verify.h
index 6a81e9b..05d7b81 100644
--- a/verify.h
+++ b/verify.h
@@ -74,7 +74,8 @@
 extern int __must_check get_next_verify(struct thread_data *td, struct io_u *);
 extern int __must_check verify_io_u(struct thread_data *, struct io_u *);
 extern int verify_io_u_async(struct thread_data *, struct io_u *);
-extern void fill_pattern(struct thread_data *td, void *p, unsigned int len, struct io_u *io_u, unsigned long seed, int use_seed);
+extern void fill_verify_pattern(struct thread_data *td, void *p, unsigned int len, struct io_u *io_u, unsigned long seed, int use_seed);
+extern void fill_buffer_pattern(struct thread_data *td, void *p, unsigned int len);
 extern void fio_verify_init(struct thread_data *td);
 
 /*