Add option for io_limit

'size' denotes both the size of the region for IO, as well as the
amount of IO to transfer. Sometimes it's useful to be able to set
them separately instead. With this patch, you can do:

size=100G
io_limit=10G

and have fio do 10G of IO between 0..100G of the address space.

Signed-off-by: Jens Axboe <axboe@fb.com>
diff --git a/HOWTO b/HOWTO
index f74360d..1c89d75 100644
--- a/HOWTO
+++ b/HOWTO
@@ -430,6 +430,14 @@
 		is given, fio will use 20% of the full size of the given
 		files or devices.
 
+io_limit=int	Normally fio operates within the region set by 'size', which
+		means that the 'size' option sets both the region and size of
+		IO to be performed. Sometimes that is not what you want. With
+		this option, it is possible to define just the amount of IO
+		that fio should do. For instance, if 'size' is set to 20G and
+		'io_limit' is set to 5G, fio will perform IO within the first
+		20G but exit when 5G have been done.
+
 filesize=int	Individual file sizes. May be a range, in which case fio
 		will select sizes for files at random within the given range
 		and limited to 'size' in total (if that is given). If not
diff --git a/backend.c b/backend.c
index 12e562b..e0f8aa7 100644
--- a/backend.c
+++ b/backend.c
@@ -645,7 +645,7 @@
 
 static int io_bytes_exceeded(struct thread_data *td)
 {
-	unsigned long long bytes;
+	unsigned long long bytes, limit;
 
 	if (td_rw(td))
 		bytes = td->this_io_bytes[DDIR_READ] + td->this_io_bytes[DDIR_WRITE];
@@ -656,7 +656,12 @@
 	else
 		bytes = td->this_io_bytes[DDIR_TRIM];
 
-	return bytes >= td->o.size || exceeds_number_ios(td);
+	if (td->o.io_limit)
+		limit = td->o.io_limit;
+	else
+		limit = td->o.size;
+
+	return bytes >= limit || exceeds_number_ios(td);
 }
 
 /*
@@ -1141,6 +1146,8 @@
 
 static int keep_running(struct thread_data *td)
 {
+	unsigned long long limit;
+
 	if (td->done)
 		return 0;
 	if (td->o.time_based)
@@ -1152,14 +1159,19 @@
 	if (exceeds_number_ios(td))
 		return 0;
 
-	if (td->o.size != -1ULL && ddir_rw_sum(td->io_bytes) < td->o.size) {
+	if (td->o.io_limit)
+		limit = td->o.io_limit;
+	else
+		limit = td->o.size;
+
+	if (limit != -1ULL && ddir_rw_sum(td->io_bytes) < limit) {
 		uint64_t diff;
 
 		/*
 		 * If the difference is less than the minimum IO size, we
 		 * are done.
 		 */
-		diff = td->o.size - ddir_rw_sum(td->io_bytes);
+		diff = limit - ddir_rw_sum(td->io_bytes);
 		if (diff < td_max_bs(td))
 			return 0;
 
diff --git a/cconv.c b/cconv.c
index ee3b0ca..2f7177d 100644
--- a/cconv.c
+++ b/cconv.c
@@ -80,6 +80,7 @@
 	o->iodepth_batch = le32_to_cpu(top->iodepth_batch);
 	o->iodepth_batch_complete = le32_to_cpu(top->iodepth_batch_complete);
 	o->size = le64_to_cpu(top->size);
+	o->io_limit = le64_to_cpu(top->io_limit);
 	o->size_percent = le32_to_cpu(top->size_percent);
 	o->fill_device = le32_to_cpu(top->fill_device);
 	o->file_append = le32_to_cpu(top->file_append);
@@ -428,6 +429,7 @@
 	memcpy(top->buffer_pattern, o->buffer_pattern, MAX_PATTERN_SIZE);
 
 	top->size = __cpu_to_le64(o->size);
+	top->io_limit = __cpu_to_le64(o->io_limit);
 	top->verify_backlog = __cpu_to_le64(o->verify_backlog);
 	top->start_delay = __cpu_to_le64(o->start_delay);
 	top->start_delay_high = __cpu_to_le64(o->start_delay_high);
diff --git a/filesetup.c b/filesetup.c
index 79eea9f..b6cf2e7 100644
--- a/filesetup.c
+++ b/filesetup.c
@@ -944,8 +944,12 @@
 	 * iolog already set the total io size, if we read back
 	 * stored entries.
 	 */
-	if (!o->read_iolog_file)
-		td->total_io_size = o->size * o->loops;
+	if (!o->read_iolog_file) {
+		if (o->io_limit)
+			td->total_io_size = o->io_limit * o->loops;
+		else
+			td->total_io_size = o->size * o->loops;
+	}
 
 done:
 	if (o->create_only)
diff --git a/fio.1 b/fio.1
index 8cf3778..eeb036e 100644
--- a/fio.1
+++ b/fio.1
@@ -369,8 +369,16 @@
 divided between the available files for the job. If not set, fio will use the
 full size of the given files or devices. If the files do not exist, size
 must be given. It is also possible to give size as a percentage between 1 and
-100. If size=20% is given, fio will use 20% of the full size of the given files
-or devices.
+100. If size=20% is given, fio will use 20% of the full size of the given
+files or devices.
+.TP
+.BI io_limit \fR=\fPint
+Normally fio operates within the region set by \fBsize\fR, which means that
+the \fBsize\fR option sets both the region and size of IO to be performed.
+Sometimes that is not what you want. With this option, it is possible to
+define just the amount of IO that fio should do. For instance, if \fBsize\fR
+is set to 20G and \fBio_limit\fR is set to 5G, fio will perform IO within
+the first 20G but exit when 5G have been done.
 .TP
 .BI fill_device \fR=\fPbool "\fR,\fB fill_fs" \fR=\fPbool
 Sets size to something really large and waits for ENOSPC (no space left on
diff --git a/options.c b/options.c
index 50af7b4..163c5fc 100644
--- a/options.c
+++ b/options.c
@@ -1595,6 +1595,15 @@
 		.group	= FIO_OPT_G_INVALID,
 	},
 	{
+		.name	= "io_limit",
+		.lname	= "IO Limit",
+		.type	= FIO_OPT_STR_VAL,
+		.off1	= td_var_offset(io_limit),
+		.interval = 1024 * 1024,
+		.category = FIO_OPT_C_IO,
+		.group	= FIO_OPT_G_INVALID,
+	},
+	{
 		.name	= "fill_device",
 		.lname	= "Fill device",
 		.alias	= "fill_fs",
diff --git a/server.h b/server.h
index 2958e73..4f09f28 100644
--- a/server.h
+++ b/server.h
@@ -38,7 +38,7 @@
 };
 
 enum {
-	FIO_SERVER_VER			= 33,
+	FIO_SERVER_VER			= 34,
 
 	FIO_SERVER_MAX_FRAGMENT_PDU	= 1024,
 	FIO_SERVER_MAX_CMD_MB		= 2048,
diff --git a/thread_options.h b/thread_options.h
index 4642120..41b6e54 100644
--- a/thread_options.h
+++ b/thread_options.h
@@ -52,6 +52,7 @@
 	unsigned int iodepth_batch_complete;
 
 	unsigned long long size;
+	unsigned long long io_limit;
 	unsigned int size_percent;
 	unsigned int fill_device;
 	unsigned int file_append;
@@ -280,6 +281,7 @@
 	uint32_t iodepth_batch_complete;
 
 	uint64_t size;
+	uint64_t io_limit;
 	uint32_t size_percent;
 	uint32_t fill_device;
 	uint32_t file_append;