Add kb_base option to specify the base unit of a kilobyte

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/HOWTO b/HOWTO
index 2d155aa..6cdd71d 100644
--- a/HOWTO
+++ b/HOWTO
@@ -290,6 +290,11 @@
 		IO's, instead of for every IO. Use rw=randread:8 to specify
 		that.
 
+kb_base=int	The base unit for a kilobyte. The defacto base is 2^10, 1024.
+		Storage manufacturers like to use 10^3 or 1000 as a base
+		ten unit instead, for obvious reasons. Allow values are
+		1024 or 1000, with 1024 being the default.
+
 randrepeat=bool	For random IO workloads, seed the generator in a predictable
 		way so that results are repeatable across repetitions.
 
diff --git a/fio.1 b/fio.1
index f4a3293..497db53 100644
--- a/fio.1
+++ b/fio.1
@@ -172,6 +172,11 @@
 `:\fIint\fR' to the pattern type.  The default is 1.
 .RE
 .TP
+.BI kb_base \fR=\fPint
+The base unit for a kilobyte. The defacto base is 2^10, 1024.  Storage
+manufacturers like to use 10^3 or 1000 as a base ten unit instead, for obvious
+reasons. Allow values are 1024 or 1000, with 1024 being the default.
+.TP
 .BI randrepeat \fR=\fPbool
 Seed the random number generator in a predictable way so results are repeatable
 across runs.  Default: true.
diff --git a/fio.h b/fio.h
index fb70b46..56d3101 100644
--- a/fio.h
+++ b/fio.h
@@ -42,6 +42,7 @@
 	unsigned long long max_bw[2], min_bw[2];
 	unsigned long long io_kb[2];
 	unsigned long long agg[2];
+	unsigned int kb_base;
 };
 
 /*
@@ -118,6 +119,8 @@
 	unsigned continue_on_error;
 	unsigned long total_err_count;
 	int first_error;
+
+	unsigned int kb_base;
 };
 
 struct bssplit {
@@ -134,6 +137,7 @@
 	char *opendir;
 	char *ioengine;
 	enum td_ddir td_ddir;
+	unsigned int kb_base;
 	unsigned int ddir_nr;
 	unsigned int iodepth;
 	unsigned int iodepth_low;
diff --git a/options.c b/options.c
index 0954ccd..c273da7 100644
--- a/options.c
+++ b/options.c
@@ -627,6 +627,19 @@
 	return 0;
 }
 
+static int kb_base_verify(struct fio_option *o, void *data)
+{
+	struct thread_data *td = data;
+
+	if (td->o.kb_base != 1024 && td->o.kb_base != 1000) {
+		log_err("fio: kb_base set to nonsensical value: %u\n",
+				td->o.kb_base);
+		return 1;
+	}
+
+	return 0;
+}
+
 #define __stringify_1(x)	#x
 #define __stringify(x)		__stringify_1(x)
 
@@ -662,6 +675,14 @@
 		.help	= "File(s) to use for the workload",
 	},
 	{
+		.name	= "kb_base",
+		.type	= FIO_OPT_INT,
+		.off1	= td_var_offset(kb_base),
+		.help	= "How many bytes per KB for reporting (1000 or 1024)",
+		.verify	= kb_base_verify,
+		.def	= "1024",
+	},
+	{
 		.name	= "lockfile",
 		.type	= FIO_OPT_STR,
 		.cb	= str_lockfile_cb,
diff --git a/stat.c b/stat.c
index 7319b9c..c52620d 100644
--- a/stat.c
+++ b/stat.c
@@ -59,13 +59,15 @@
 	log_info("\nRun status group %d (all jobs):\n", id);
 
 	for (i = 0; i <= DDIR_WRITE; i++) {
+		const int i2p = is_power_of_2(rs->kb_base);
+
 		if (!rs->max_run[i])
 			continue;
 
-		p1 = num2str(rs->io_kb[i], 6, 1024, 1);
-		p2 = num2str(rs->agg[i], 6, 1024, 1);
-		p3 = num2str(rs->min_bw[i], 6, 1024, 1);
-		p4 = num2str(rs->max_bw[i], 6, 1024, 1);
+		p1 = num2str(rs->io_kb[i], 6, rs->kb_base, i2p);
+		p2 = num2str(rs->agg[i], 6, rs->kb_base, i2p);
+		p3 = num2str(rs->min_bw[i], 6, rs->kb_base, i2p);
+		p4 = num2str(rs->max_bw[i], 6, rs->kb_base, i2p);
 
 		log_info("%s: io=%sB, aggrb=%sB/s, minb=%sB/s, maxb=%sB/s,"
 			 " mint=%llumsec, maxt=%llumsec\n", ddir_str[i], p1, p2,
@@ -153,15 +155,17 @@
 	unsigned long long bw, iops;
 	double mean, dev;
 	char *io_p, *bw_p, *iops_p;
+	int i2p;
 
 	if (!ts->runtime[ddir])
 		return;
 
+	i2p = is_power_of_2(rs->kb_base);
 	runt = ts->runtime[ddir];
 
 	bw = (1000 * ts->io_bytes[ddir]) / runt;
-	io_p = num2str(ts->io_bytes[ddir] >> 10, 6, 1024, 1);
-	bw_p = num2str(bw >> 10, 6, 1024, 1);
+	io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
+	bw_p = num2str(bw, 6, 1, i2p);
 
 	iops = (1000 * ts->total_io_u[ddir]) / runt;
 	iops_p = num2str(iops, 6, 1, 0);
@@ -457,6 +461,7 @@
 	struct thread_data *td;
 	struct thread_stat *threadstats, *ts;
 	int i, j, k, l, nr_ts, last_ts, idx;
+	int kb_base_warned = 0;
 
 	runstats = malloc(sizeof(struct group_run_stats) * (groupid + 1));
 
@@ -529,6 +534,12 @@
 			 * first pid in group, not very useful...
 			 */
 			ts->pid = td->pid;
+
+			ts->kb_base = td->o.kb_base;
+		} else if (ts->kb_base != td->o.kb_base && !kb_base_warned) {
+			log_info("fio: kb_base differs for jobs in group, using"
+				 " %u as the base\n", ts->kb_base);
+			kb_base_warned = 1;
 		}
 
 		ts->continue_on_error = td->o.continue_on_error;
@@ -590,6 +601,7 @@
 
 		ts = &threadstats[i];
 		rs = &runstats[ts->groupid];
+		rs->kb_base = ts->kb_base;
 
 		for (j = 0; j <= DDIR_WRITE; j++) {
 			if (!ts->runtime[j])
@@ -603,7 +615,7 @@
 			if (ts->runtime[j]) {
 				unsigned long runt;
 
-				runt = ts->runtime[j] * 1024 / 1000;
+				runt = ts->runtime[j];
 				bw = ts->io_bytes[j] / runt;
 			}
 			if (bw < rs->min_bw[j])
@@ -611,7 +623,7 @@
 			if (bw > rs->max_bw[j])
 				rs->max_bw[j] = bw;
 
-			rs->io_kb[j] += ts->io_bytes[j] >> 10;
+			rs->io_kb[j] += ts->io_bytes[j] / rs->kb_base;
 		}
 	}
 
@@ -619,13 +631,13 @@
 		unsigned long max_run[2];
 
 		rs = &runstats[i];
-		max_run[0] = rs->max_run[0] * 1024 / 1000;
-		max_run[1] = rs->max_run[1] * 1024 / 1000;
+		max_run[0] = rs->max_run[0];
+		max_run[1] = rs->max_run[1];
 
 		if (rs->max_run[0])
-			rs->agg[0] = (rs->io_kb[0]*1024) / max_run[0];
+			rs->agg[0] = (rs->io_kb[0] * 1000) / max_run[0];
 		if (rs->max_run[1])
-			rs->agg[1] = (rs->io_kb[1]*1024) / max_run[1];
+			rs->agg[1] = (rs->io_kb[1] * 1000) / max_run[1];
 	}
 
 	/*