Change network transmitted doubles to fio_fp64_t IEEE 754 type

Encode and decode when transferring over the network.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/client.c b/client.c
index 97bfc89..d538e8a 100644
--- a/client.c
+++ b/client.c
@@ -330,9 +330,12 @@
 	dst->max_val	= le64_to_cpu(src->max_val);
 	dst->min_val	= le64_to_cpu(src->min_val);
 	dst->samples	= le64_to_cpu(src->samples);
-	/* FIXME */
-	dst->mean	= __le64_to_cpu(src->mean);
-	dst->S		= __le64_to_cpu(src->S);
+
+	/*
+	 * Floats arrive as IEEE 754 encoded uint64_t, convert back to double
+	 */
+	dst->mean.u.f	= fio_uint64_to_double(le64_to_cpu(dst->mean.u.i));
+	dst->S.u.f	= fio_uint64_to_double(le64_to_cpu(dst->S.u.i));
 }
 
 static void convert_ts(struct thread_stat *dst, struct thread_stat *src)
@@ -357,7 +360,13 @@
 	dst->minf		= le64_to_cpu(src->minf);
 	dst->majf		= le64_to_cpu(src->majf);
 	dst->clat_percentiles	= le64_to_cpu(src->clat_percentiles);
-	dst->percentile_list	= NULL;
+
+	for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) {
+		fio_fp64_t *fps = &src->percentile_list[i];
+		fio_fp64_t *fpd = &dst->percentile_list[i];
+
+		fpd->u.f = fio_uint64_to_double(le64_to_cpu(fps->u.i));
+	}
 
 	for (i = 0; i < FIO_IO_U_MAP_NR; i++) {
 		dst->io_u_map[i]	= le32_to_cpu(src->io_u_map[i]);
diff --git a/fio.h b/fio.h
index dce4d8d..1fb2118 100644
--- a/fio.h
+++ b/fio.h
@@ -197,7 +197,7 @@
 	unsigned long long trim_backlog;
 	unsigned int clat_percentiles;
 	unsigned int overwrite_plist;
-	double percentile_list[FIO_IO_U_LIST_MAX_LEN];
+	fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
 
 	char *read_iolog_file;
 	char *write_iolog_file;
@@ -498,6 +498,7 @@
 extern int is_backend;
 extern int nr_clients;
 extern int log_syslog;
+extern const fio_fp64_t def_percentile_list[FIO_IO_U_LIST_MAX_LEN];
 
 extern struct thread_data *threads;
 
diff --git a/ieee754.c b/ieee754.c
index 0fbc7f0..c7742a2 100644
--- a/ieee754.c
+++ b/ieee754.c
@@ -6,6 +6,7 @@
  * Below code was granted to the public domain.
  */
 #include <inttypes.h>
+#include "ieee754.h"
 
 uint64_t pack754(long double f, unsigned bits, unsigned expbits)
 {
diff --git a/ieee754.h b/ieee754.h
index 2087da5..5af9518 100644
--- a/ieee754.h
+++ b/ieee754.h
@@ -1,10 +1,20 @@
 #ifndef FIO_IEEE754_H
 #define FIO_IEEE754_H
 
+#include <inttypes.h>
+
 extern uint64_t pack754(long double f, unsigned bits, unsigned expbits);
 extern long double unpack754(uint64_t i, unsigned bits, unsigned expbits);
 
 #define fio_double_to_uint64(val)	pack754((val), 64, 11)
 #define fio_uint64_to_double(val)	unpack754((val), 64, 11)
 
+typedef struct fio_fp64 {
+	union {
+		uint64_t i;
+		double f;
+		uint8_t filler[16];
+	} u;
+} fio_fp64_t;
+
 #endif
diff --git a/init.c b/init.c
index 2341915..6066fc5 100644
--- a/init.c
+++ b/init.c
@@ -69,6 +69,24 @@
 
 static char cmd_optstr[256];
 
+const fio_fp64_t def_percentile_list[FIO_IO_U_LIST_MAX_LEN] = {
+	{ .u.f	=  1.0 },
+	{ .u.f	=  5.0 },
+	{ .u.f	= 10.0 },
+	{ .u.f	= 20.0 },
+	{ .u.f	= 30.0 },
+	{ .u.f	= 40.0 },
+	{ .u.f	= 50.0 },
+	{ .u.f	= 60.0 },
+	{ .u.f	= 70.0 },
+	{ .u.f	= 80.0 },
+	{ .u.f	= 90.0 },
+	{ .u.f	= 95.0 },
+	{ .u.f	= 99.0 },
+	{ .u.f	= 99.5 },
+	{ .u.f	= 99.9 },
+};
+
 /*
  * Command line options. These will contain the above, plus a few
  * extra that only pertain to fio itself and not jobs.
@@ -718,9 +736,9 @@
 
 	td->ts.clat_percentiles = td->o.clat_percentiles;
 	if (td->o.overwrite_plist)
-		td->ts.percentile_list = td->o.percentile_list;
+		memcpy(td->ts.percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
 	else
-		td->ts.percentile_list = NULL;
+		memcpy(td->ts.percentile_list, def_percentile_list, sizeof(def_percentile_list));
 
 	td->ts.clat_stat[0].min_val = td->ts.clat_stat[1].min_val = ULONG_MAX;
 	td->ts.slat_stat[0].min_val = td->ts.slat_stat[1].min_val = ULONG_MAX;
diff --git a/iolog.h b/iolog.h
index fa0a218..dd4d647 100644
--- a/iolog.h
+++ b/iolog.h
@@ -1,6 +1,8 @@
 #ifndef FIO_IOLOG_H
 #define FIO_IOLOG_H
 
+#include "ieee754.h"
+
 /*
  * Use for maintaining statistics
  */
@@ -9,8 +11,8 @@
 	uint64_t min_val;
 	uint64_t samples;
 
-	double mean;
-	double S;
+	fio_fp64_t mean;
+	fio_fp64_t S;
 };
 
 /*
diff --git a/server.c b/server.c
index 29e2387..0f64c23 100644
--- a/server.c
+++ b/server.c
@@ -18,6 +18,7 @@
 #include "fio.h"
 #include "server.h"
 #include "crc/crc16.h"
+#include "ieee754.h"
 
 int fio_net_port = 8765;
 
@@ -464,9 +465,12 @@
 	dst->max_val	= cpu_to_le64(src->max_val);
 	dst->min_val	= cpu_to_le64(src->min_val);
 	dst->samples	= cpu_to_le64(src->samples);
-	/* FIXME */
-	dst->mean	= __cpu_to_le64(src->mean);
-	dst->S		= __cpu_to_le64(src->S);
+
+	/*
+	 * Encode to IEEE 754 for network transfer
+	 */
+	dst->mean.u.i	= __cpu_to_le64(fio_double_to_uint64(src->mean.u.f));
+	dst->S.u.i	= __cpu_to_le64(fio_double_to_uint64(src->S.u.f));
 }
 
 static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src)
@@ -521,7 +525,12 @@
 	p.ts.minf		= cpu_to_le64(ts->minf);
 	p.ts.majf		= cpu_to_le64(ts->majf);
 	p.ts.clat_percentiles	= cpu_to_le64(ts->clat_percentiles);
-	p.ts.percentile_list	= NULL;
+
+	for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) {
+		fio_fp64_t *fp = &p.ts.percentile_list[i];
+
+		fp->u.i = __cpu_to_le64(fio_double_to_uint64(fp->u.f));
+	}
 
 	for (i = 0; i < FIO_IO_U_MAP_NR; i++) {
 		p.ts.io_u_map[i]	= cpu_to_le32(ts->io_u_map[i]);
diff --git a/stat.c b/stat.c
index cfb2978..7bcae31 100644
--- a/stat.c
+++ b/stat.c
@@ -9,6 +9,7 @@
 
 #include "fio.h"
 #include "diskutil.h"
+#include "ieee754.h"
 
 void update_rusage_stat(struct thread_data *td)
 {
@@ -101,13 +102,13 @@
 
 static int double_cmp(const void *a, const void *b)
 {
-	const double fa = *(const double *)a;
-	const double fb = *(const double *)b;
+	const fio_fp64_t fa = *(const fio_fp64_t *) a;
+	const fio_fp64_t fb = *(const fio_fp64_t *) b;
 	int cmp = 0;
 
-	if (fa > fb)
+	if (fa.u.f > fb.u.f)
 		cmp = 1;
-	else if (fa < fb)
+	else if (fa.u.f < fb.u.f)
 		cmp = -1;
 
 	return cmp;
@@ -117,37 +118,30 @@
  * Find and display the p-th percentile of clat
  */
 static void show_clat_percentiles(unsigned int *io_u_plat, unsigned long nr,
-				 double *user_list)
+				  fio_fp64_t *plist)
 {
 	unsigned long sum = 0;
 	unsigned int len, i, j = 0;
-	const double *plist;
 	int is_last = 0;
-	static const double def_list[FIO_IO_U_LIST_MAX_LEN] = {
-			1.0, 5.0, 10.0, 20.0, 30.0,
-			40.0, 50.0, 60.0, 70.0, 80.0,
-			90.0, 95.0, 99.0, 99.5, 99.9};
 
-	plist = user_list;
-	if (!plist)
-		plist = def_list;
-
-	for (len = 0; len <FIO_IO_U_LIST_MAX_LEN && plist[len] != 0; len++)
-		;
+	len = 0;
+	while (len < FIO_IO_U_LIST_MAX_LEN && plist[len].u.f != 0.0)
+		len++;
 
 	/*
-	 * Sort the user-specified list. Note that this does not work
-	 * for NaN values
+	 * Sort the percentile list. Note that it may already be sorted if
+	 * we are using the default values, but since it's a short list this
+	 * isn't a worry. Also note that this does not work for NaN values.
 	 */
-	if (user_list && len > 1)
-		qsort((void*)user_list, len, sizeof(user_list[0]), double_cmp);
+	if (len > 1)
+		qsort((void*)plist, len, sizeof(plist[0]), double_cmp);
 
 	log_info("    clat percentiles (usec) :");
 
 	for (i = 0; i < FIO_IO_U_PLAT_NR && !is_last; i++) {
 		sum += io_u_plat[i];
-		while (sum >= (plist[j] / 100 * nr)) {
-			assert(plist[j] <= 100.0);
+		while (sum >= (plist[j].u.f / 100.0 * nr)) {
+			assert(plist[j].u.f <= 100.0);
 
 			/* for formatting */
 			if (j != 0 && (j % 4) == 0)
@@ -181,10 +175,10 @@
 	*max = is->max_val;
 
 	n = (double) is->samples;
-	*mean = is->mean;
+	*mean = is->mean.u.f;
 
 	if (n > 1.0)
-		*dev = sqrt(is->S / (n - 1.0));
+		*dev = sqrt(is->S.u.f / (n - 1.0));
 	else
 		*dev = 0;
 
@@ -629,23 +623,23 @@
 	 *  #Parallel_algorithm>
 	 */
 	if (nr == 1) {
-		mean = src->mean;
-		S = src->S;
+		mean = src->mean.u.f;
+		S = src->S.u.f;
 	} else {
-		double delta = src->mean - dst->mean;
+		double delta = src->mean.u.f - dst->mean.u.f;
 
-		mean = ((src->mean * src->samples) +
-			(dst->mean * dst->samples)) /
+		mean = ((src->mean.u.f * src->samples) +
+			(dst->mean.u.f * dst->samples)) /
 			(dst->samples + src->samples);
 
-		S =  src->S + dst->S + pow(delta, 2.0) *
+		S =  src->S.u.f + dst->S.u.f + pow(delta, 2.0) *
 			(dst->samples * src->samples) /
 			(dst->samples + src->samples);
 	}
 
 	dst->samples += src->samples;
-	dst->mean = mean;
-	dst->S = S;
+	dst->mean.u.f = mean;
+	dst->S.u.f = S;
 }
 
 void show_run_stats(void)
@@ -715,9 +709,9 @@
 
 		ts->clat_percentiles = td->o.clat_percentiles;
 		if (td->o.overwrite_plist)
-			ts->percentile_list = td->o.percentile_list;
+			memcpy(ts->percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
 		else
-			ts->percentile_list = NULL;
+			memcpy(ts->percentile_list, def_percentile_list, sizeof(def_percentile_list));
 
 		idx++;
 		ts->members++;
@@ -897,10 +891,10 @@
 	if (data < is->min_val)
 		is->min_val = data;
 
-	delta = val - is->mean;
+	delta = val - is->mean.u.f;
 	if (delta) {
-		is->mean += delta / (is->samples + 1.0);
-		is->S += delta * (val - is->mean);
+		is->mean.u.f += delta / (is->samples + 1.0);
+		is->S.u.f += delta * (val - is->mean.u.f);
 	}
 
 	is->samples++;
diff --git a/stat.h b/stat.h
index 72cbe16..89766fc 100644
--- a/stat.h
+++ b/stat.h
@@ -141,7 +141,7 @@
 	 * IO depth and latency stats
 	 */
 	uint64_t clat_percentiles;
-	double *percentile_list;
+	fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
 
 	uint32_t io_u_map[FIO_IO_U_MAP_NR];
 	uint32_t io_u_submit[FIO_IO_U_MAP_NR];