Description: Fix qdio statistics.
Symptom:     qdio statistics show high usage even if only brief bursts of
             activity happen in an interval. Furthermore, the min value
             cannot be tracked in a meaningful way via the blktrace mechanism.
Problem:     Previous implementation's qdio usage was not accurate since only
             the fill level but not its duration was monitored. Plus the
             interpretation of the qdio fill level values as received via
             blktrace was wrong.
Solution:    Account both fill level and duration in-kernel, export it via
             sysfs and query it via ziomon_util. Hence this change requires
             a respective kernel patch that exports an accurate qdio
             utilization statistic via sysfs.
             Furthermore remove the full-fledged qdio statistics from the
             zfcpdd messages and report the maximum queue fill level only.
             Note that these changes require changes in the binary file format,
             which makes data files collected with previous versions
             incompatible.
---
 ziomon/ziomon_dacc.c   |    6 ++--
 ziomon/ziomon_dacc.h   |    2 -
 ziomon/ziomon_util.c   |   70 +++++++++++++++++++++++++++++++++++++++++--------
 ziomon/ziomon_util.h   |    8 ++++-
 ziomon/ziomon_zfcpdd.c |   25 +++++++++++------
 ziomon/ziomon_zfcpdd.h |    2 -
 6 files changed, 87 insertions(+), 26 deletions(-)

--- a/ziomon/ziomon_dacc.c
+++ b/ziomon/ziomon_dacc.c
@@ -372,7 +372,7 @@ int add_msg(FILE *fp, struct message *ms
 int init_file(FILE *fp, struct file_header *f_hdr)
 {
 	f_hdr->magic = DATA_MGR_MAGIC;
-	f_hdr->version = DATA_MGR_V1;
+	f_hdr->version = DATA_MGR_V2;
 	f_hdr->first_msg_offset = 0;
 	f_hdr->end_time = 0;
 
@@ -396,7 +396,7 @@ int get_header(FILE *fp, struct file_hea
 		fprintf(stderr, ZIOMON_DACC_NAME ": No valid header\n");
 		return -2;
 	}
-	if (hdr->version != DATA_MGR_V1) {
+	if (hdr->version != DATA_MGR_V2) {
 		fprintf(stderr, ZIOMON_DACC_NAME ": Wrong version\n");
 		return -2;
 	}
@@ -572,7 +572,7 @@ int write_aggr_file(FILE *fp, struct agg
 void init_aggr_data_struct(struct aggr_data *data)
 {
 	data->magic = DATA_MGR_MAGIC_AGGR;
-	data->version = DATA_MGR_V1;
+	data->version = DATA_MGR_V2;
 	data->num_zfcpdd = 0;
 	data->num_blkiomon = 0;
 	data->end_time = 0;
--- a/ziomon/ziomon_dacc.h
+++ b/ziomon/ziomon_dacc.h
@@ -17,7 +17,7 @@
 
 #define DATA_MGR_MAGIC		0x64616d67
 #define DATA_MGR_MAGIC_AGGR	0x61676772
-#define DATA_MGR_V1		1u
+#define DATA_MGR_V2		2u
 
 
 /**
--- a/ziomon/ziomon_util.c
+++ b/ziomon/ziomon_util.c
@@ -98,6 +98,8 @@ static void swap_overall_result(struct o
 		u_res = &a_res->result;
 		swap_64(u_res->count);
 		swap_32(u_res->queue_full);
+		swap_64(u_res->queue_util_integral);
+		swap_64(u_res->queue_util_interval);
 		swap_stat_variance(&u_res->adapter);
 		swap_stat_variance(&u_res->bus);
 		swap_stat_variance(&u_res->cpu);
@@ -152,6 +154,10 @@ void aggregate_utilization_data(struct o
 			continue;
 		tgt_a_res = get_result_no(tgt, src_a_res->adapter_no);
 		tgt_a_res->result.queue_full += src_a_res->result.queue_full;
+		tgt_a_res->result.queue_util_integral +=
+					src_a_res->result.queue_util_integral;
+		tgt_a_res->result.queue_util_interval +=
+					src_a_res->result.queue_util_interval;
 		tgt_a_res->result.count += src_a_res->result.count;
 		aggregate_stat_variance(&src_a_res->result.adapter,
 				&tgt_a_res->result.adapter);
@@ -237,7 +243,12 @@ int verbose=0;
 struct util_data {
 	int 			queue_full; /* # queue full instances in frame */
 	int			queue_full_prev; /* previous absolut value
-							     of queue_full */
+						    of queue_full */
+	__u64			queue_util_integral;
+	__u64			queue_util_interval;
+	__u64			queue_util_prev; /* previous absolut value
+						    of queue_util_integral */
+	struct timeval		queue_util_timestamp;
 	struct stat_variance 	adapter;
 	struct stat_variance 	bus;
 	struct stat_variance 	cpu;
@@ -466,7 +477,9 @@ static int poll_queue_full(int init, str
 	struct adapter_data *adpt;
 	struct util_data    *u_data;
 	int queue_full_tmp;
+	long long unsigned int queue_util_tmp;
 	int i;
+	struct timeval tmp, cur_time;
 
 	for (i = 0; i < all_adapters->num_adapters; ++i) {
 		adpt = &all_adapters->adapters[i];
@@ -475,20 +488,38 @@ static int poll_queue_full(int init, str
 		/* read queue_full attribute */
 		if (read_attribute(adpt->q_full_path, line, &adpt->status))
 			continue;
-		rc = sscanf(line, "%d", &queue_full_tmp);
-		if (rc != 1) {
+		rc = sscanf(line, "%d %Lu", &queue_full_tmp, &queue_util_tmp);
+		if (rc == 1) {
+			fprintf(stderr, ZIOMON_UTIL_NAME ": Only one value in"
+				" %s, your kernel level is probably too old.\n",
+				adpt->q_full_path);
+			return -1;
+		}
+		if (rc != 2) {
 			fprintf(stderr, ZIOMON_UTIL_NAME ": Warning:"
 				" Could not parse %s: %s\n", line,
 				strerror(errno));
 			adpt->status = 6;
 			continue;
 		}
+		gettimeofday(&cur_time, NULL);
 		if (!init) {
-			u_data->queue_full = queue_full_tmp - u_data->queue_full_prev;
-			verbose_msg("data read for adapter %d: queue_full=%d\n",
-				    adpt->host_nr, u_data->queue_full);
+			u_data->queue_full = queue_full_tmp
+						- u_data->queue_full_prev;
+			u_data->queue_util_integral = queue_util_tmp
+						- u_data->queue_util_prev;
+			timersub(&cur_time, &u_data->queue_util_timestamp,
+						&tmp);
+			u_data->queue_util_interval = tmp.tv_sec * 1000000
+							+ tmp.tv_usec;
+			verbose_msg("data read for adapter %d: queue_full=%d,"
+				    " queue_util=%Lu\n", adpt->host_nr,
+				    u_data->queue_full, (long long unsigned int)
+				    u_data->queue_util_integral);
 		}
 		u_data->queue_full_prev = queue_full_tmp;
+		u_data->queue_util_prev = queue_util_tmp;
+		u_data->queue_util_timestamp = cur_time;
 	}
 
 	return 0;
@@ -530,6 +561,15 @@ static int poll_ioerr_cnt(int init, stru
 }
 
 
+static void reinit_adapters(struct adapters *adptrs)
+{
+	int i;
+
+	for (i = 0; i < adptrs->num_adapters; ++i)
+		init_util_data(&adptrs->adapters[i].data);
+}
+
+
 static int init_adapters(struct adapters *all_adapters, struct options *opts)
 {
 	struct adapter_data *adapter;
@@ -553,11 +593,8 @@ static int init_adapters(struct adapters
 		}
 		init_util_data(&all_adapters->adapters[i].data);
 	}
-	if (poll_queue_full(1, all_adapters, opts)) {
-		fprintf(stderr, ZIOMON_UTIL_NAME ": Error: Could not read "
-			"initial values of queue_full attributes\n");
+	if (poll_queue_full(1, all_adapters, opts))
 		return -1;
-	}
 
 	return 0;
 }
@@ -826,6 +863,10 @@ static void generate_result(struct overa
 		if (a_res->valid) {
 			u_res = &a_res->result;
 			u_res->queue_full = u_data->queue_full;
+			u_res->queue_util_integral
+						= u_data->queue_util_integral;
+			u_res->queue_util_interval
+						= u_data->queue_util_interval;
 			u_res->count = u_data->count;
 			copy_stat_variance(&u_res->adapter, &u_data->adapter);
 			copy_stat_variance(&u_res->bus, &u_data->bus);
@@ -1092,6 +1133,13 @@ static int has_adapter_traffic(struct ov
 			return 1;
 		if (a_res->result.queue_full != 0)
 			return 1;
+		/* this makes sure that we don't send messages when essentially
+		   no data is processed - reading the 'utilization' attribute
+		   causes always traffic, hence we would always send a message
+		   without this threshold! */
+		if (a_res->result.queue_util_integral
+			/ (double)a_res->result.queue_util_interval >= 0.05)
+			return 1;
 		if (has_non_null_stats(&a_res->result.adapter)
 		    || has_non_null_stats(&a_res->result.bus)
 		    || has_non_null_stats(&a_res->result.cpu))
@@ -1226,7 +1274,7 @@ int main(int argc, char **argv)
 	duration_end.tv_sec += opts.duration;
 	do {
 		interval_end.tv_sec += opts.i_duration;
-		init_adapters(all_adapters, NULL);
+		reinit_adapters(all_adapters);
 		do {
 			sample_end.tv_sec += opts.s_duration;
 			sleep_until(&sample_end);
--- a/ziomon/ziomon_util.h
+++ b/ziomon/ziomon_util.h
@@ -18,11 +18,15 @@
 
 struct util_result {
 	__u64			count;
-	__u32			queue_full;	/* number of queue_full
-	                           instances within the given timeframe */
 	struct stat_variance 	adapter;
 	struct stat_variance 	bus;
 	struct stat_variance 	cpu;
+	__u64			queue_util_integral; /* integral of the queue
+					utilization over microseconds */
+	__u64			queue_util_interval; /* interval length
+							in microseconds */
+	__u32			queue_full;	/* number of queue_full
+	      instances within the given timeframe */
 } __attribute__ ((packed));
 
 struct adapter_result {
--- a/ziomon/ziomon_zfcpdd.c
+++ b/ziomon/ziomon_zfcpdd.c
@@ -21,6 +21,7 @@
 #include <sys/stat.h>
 #include <sys/msg.h>
 #include <limits.h>
+#include <stdint.h>
 
 #include "blktrace.h"
 #include "ziomon_zfcpdd.h"
@@ -40,7 +41,7 @@ static void swap_dstat(struct zfcpdd_dst
 	swap_stat_variance(&stat->chan_lat);
 	swap_stat_variance(&stat->fabr_lat);
 	swap_stat_variance(&stat->inb);
-	swap_stat_variance(&stat->outb);
+	swap_16(stat->outb_max);
 	for (i=0; i<BLKIOMON_CHAN_LAT_BUCKETS; ++i)
 		swap_32(stat->chan_lat_hist[i]);
 	for (i=0; i<BLKIOMON_FABR_LAT_BUCKETS; ++i)
@@ -70,10 +71,12 @@ void aggregate_dstat(struct zfcpdd_dstat
 	aggregate_stat_variance(&src->chan_lat, &tgt->chan_lat);
 	aggregate_stat_variance(&src->fabr_lat, &tgt->fabr_lat);
 	aggregate_stat_variance(&src->inb, &tgt->inb);
-	aggregate_stat_variance(&src->outb, &tgt->outb);
+	if (src->outb_max > tgt->outb_max)
+		tgt->outb_max = src->outb_max;
 }
 
-void zfcpdd_print_stats(struct zfcpdd_dstat *stat) {
+void zfcpdd_print_stats(struct zfcpdd_dstat *stat)
+{
 	int i;
 
 	printf("timestamp     : %s", ctime((time_t*)&stat->time));
@@ -91,8 +94,7 @@ void zfcpdd_print_stats(struct zfcpdd_ds
 		printf(" %u", stat->fabr_lat_hist[i]);
 	printf("\n\tinbound latency:\n");
 	print_stat_variance(&stat->inb, stat->count);
-	printf("\toutbound latency:\n");
-	print_stat_variance(&stat->outb, stat->count);
+	printf("\toutbound q max   : %hu\n", stat->outb_max);
 }
 
 #ifdef WITH_MAIN
@@ -171,7 +173,6 @@ static struct dstat *zfcpdd_dstat_alloc(
 	init_stat_variance(&dstat->msg.stat.chan_lat);
 	init_stat_variance(&dstat->msg.stat.fabr_lat);
 	init_stat_variance(&dstat->msg.stat.inb);
-	init_stat_variance(&dstat->msg.stat.outb);
 
 	return dstat;
 }
@@ -218,6 +219,14 @@ static void zfcpdd_account_hist_log2(__u
 	bucket[index]++;
 }
 
+static void zfcpdd_account_outb(struct zfcpdd_dstat *stat,
+					struct zfcp_blk_drv_data *dd)
+{
+	dd->outb_usage = 128 - dd->outb_usage;
+	if (dd->outb_usage > stat->outb_max)
+		stat->outb_max = dd->outb_usage;
+}
+
 static int zfcpdd_account(struct blk_io_trace *bit,
 			     struct zfcp_blk_drv_data *dd)
 {
@@ -245,7 +254,7 @@ static int zfcpdd_account(struct blk_io_
 	update_stat_variance(&stat->chan_lat, dd->chan_lat);
 	update_stat_variance(&stat->fabr_lat, dd->fabr_lat / 1000);
 	update_stat_variance(&stat->inb, dd->inb_usage);
-	update_stat_variance(&stat->outb, dd->outb_usage);
+	zfcpdd_account_outb(stat, dd);
 	zfcpdd_account_hist_log2(stat->chan_lat_hist, dd->chan_lat,
 				    &clat);
 	zfcpdd_account_hist_log2(stat->fabr_lat_hist, dd->fabr_lat / 1000,
@@ -325,7 +334,7 @@ static int zfcpdd_output_ascii(struct ds
 	fprintf(fp, "device: %d\t", p->device);
 	fprintf(fp, "interval end: %ld\n", (unsigned long)p->time);
 
-	print_var(fp, "outbound", &p->outb);
+	fprintf(fp, "outbound q max: %hu", p->outb_max);
 	print_var(fp, "inbound", &p->inb);
 	print_var(fp, "channel latency", &p->chan_lat);
 	print_var(fp, "fabric latency", &p->fabr_lat);
--- a/ziomon/ziomon_zfcpdd.h
+++ b/ziomon/ziomon_zfcpdd.h
@@ -23,9 +23,9 @@ struct zfcpdd_dstat {
 	struct stat_variance chan_lat;
 	struct stat_variance fabr_lat;
 	struct stat_variance inb;
-	struct stat_variance outb;
 	__u64 count;
 	__u32 device;	/* device identifier */
+	__u16 outb_max;
 } __attribute__ ((packed));
 
 struct hist_log2 {
