Add support for trim as a workload type

This only works on Linux so far, and it's always sync given what
the interface to the kernel looks like. Also restricted to pure
block devices.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/stat.c b/stat.c
index 0a04798..e29bf0c 100644
--- a/stat.c
+++ b/stat.c
@@ -259,12 +259,12 @@
 void show_group_stats(struct group_run_stats *rs)
 {
 	char *p1, *p2, *p3, *p4;
-	const char *ddir_str[] = { "   READ", "  WRITE" };
+	const char *ddir_str[] = { "   READ", "  WRITE" , "   TRIM"};
 	int i;
 
 	log_info("\nRun status group %d (all jobs):\n", rs->groupid);
 
-	for (i = 0; i <= DDIR_WRITE; i++) {
+	for (i = 0; i < DDIR_RWDIR_CNT; i++) {
 		const int i2p = is_power_of_2(rs->kb_base);
 
 		if (!rs->max_run[i])
@@ -288,7 +288,8 @@
 }
 
 #define ts_total_io_u(ts)	\
-	((ts)->total_io_u[0] + (ts)->total_io_u[1])
+	((ts)->total_io_u[DDIR_READ] + (ts)->total_io_u[DDIR_WRITE] +\
+		(ts)->total_io_u[DDIR_TRIM])
 
 static void stat_calc_dist(unsigned int *map, unsigned long total,
 			   double *io_u_dist)
@@ -356,7 +357,7 @@
 static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts,
 			     int ddir)
 {
-	const char *ddir_str[] = { "read ", "write" };
+	const char *ddir_str[] = { "read ", "write", "trim" };
 	unsigned long min, max, runt;
 	unsigned long long bw, iops;
 	double mean, dev;
@@ -526,8 +527,9 @@
 	time_t time_p;
 	char time_buf[64];
 
-	if (!(ts->io_bytes[0] + ts->io_bytes[1]) &&
-	    !(ts->total_io_u[0] + ts->total_io_u[1]))
+	if (!(ts->io_bytes[DDIR_READ] + ts->io_bytes[DDIR_WRITE] +
+	    ts->io_bytes[DDIR_TRIM]) && !(ts->total_io_u[DDIR_READ] +
+	    ts->total_io_u[DDIR_WRITE] + ts->total_io_u[DDIR_TRIM]))
 		return;
 
 	time(&time_p);
@@ -551,6 +553,8 @@
 		show_ddir_status(rs, ts, DDIR_READ);
 	if (ts->io_bytes[DDIR_WRITE])
 		show_ddir_status(rs, ts, DDIR_WRITE);
+	if (ts->io_bytes[DDIR_TRIM])
+		show_ddir_status(rs, ts, DDIR_TRIM);
 
 	stat_calc_lat_u(ts, io_u_lat_u);
 	stat_calc_lat_m(ts, io_u_lat_m);
@@ -686,9 +690,11 @@
 	/* General Info */
 	log_info("2;%s;%d;%d", ts->name, ts->groupid, ts->error);
 	/* Log Read Status */
-	show_ddir_status_terse(ts, rs, 0);
+	show_ddir_status_terse(ts, rs, DDIR_READ);
 	/* Log Write Status */
-	show_ddir_status_terse(ts, rs, 1);
+	show_ddir_status_terse(ts, rs, DDIR_WRITE);
+	/* Log Trim Status */
+	show_ddir_status_terse(ts, rs, DDIR_TRIM);
 
 	/* CPU Usage */
 	if (ts->total_run_time) {
@@ -747,9 +753,11 @@
 	log_info("%s;%s;%s;%d;%d", FIO_TERSE_VERSION, fio_version_string,
 					ts->name, ts->groupid, ts->error);
 	/* Log Read Status */
-	show_ddir_status_terse(ts, rs, 0);
+	show_ddir_status_terse(ts, rs, DDIR_READ);
 	/* Log Write Status */
-	show_ddir_status_terse(ts, rs, 1);
+	show_ddir_status_terse(ts, rs, DDIR_WRITE);
+	/* Log Trim Status */
+	show_ddir_status_terse(ts, rs, DDIR_TRIM);
 
 	/* CPU Usage */
 	if (ts->total_run_time) {
@@ -846,7 +854,7 @@
 {
 	int i;
 
-	for (i = 0; i < 2; i++) {
+	for (i = 0; i < DDIR_RWDIR_CNT; i++) {
 		if (dst->max_run[i] < src->max_run[i])
 			dst->max_run[i] = src->max_run[i];
 		if (dst->min_run[i] && dst->min_run[i] > src->min_run[i])
@@ -866,7 +874,7 @@
 {
 	int l, k;
 
-	for (l = 0; l <= DDIR_WRITE; l++) {
+	for (l = 0; l < DDIR_RWDIR_CNT; l++) {
 		sum_stat(&dst->clat_stat[l], &src->clat_stat[l], nr);
 		sum_stat(&dst->slat_stat[l], &src->slat_stat[l], nr);
 		sum_stat(&dst->lat_stat[l], &src->lat_stat[l], nr);
@@ -895,12 +903,12 @@
 	for (k = 0; k < FIO_IO_U_LAT_M_NR; k++)
 		dst->io_u_lat_m[k] += src->io_u_lat_m[k];
 
-	for (k = 0; k <= 2; k++) {
+	for (k = 0; k < DDIR_RWDIR_CNT; k++) {
 		dst->total_io_u[k] += src->total_io_u[k];
 		dst->short_io_u[k] += src->short_io_u[k];
 	}
 
-	for (k = 0; k <= DDIR_WRITE; k++) {
+	for (k = 0; k < DDIR_RWDIR_CNT; k++) {
 		int m;
 		for (m = 0; m < FIO_IO_U_PLAT_NR; m++)
 			dst->io_u_plat[k][m] += src->io_u_plat[k][m];
@@ -913,9 +921,11 @@
 
 void init_group_run_stat(struct group_run_stats *gs)
 {
+	int i;
 	memset(gs, 0, sizeof(*gs));
-	gs->min_bw[0] = gs->min_run[0] = ~0UL;
-	gs->min_bw[1] = gs->min_run[1] = ~0UL;
+
+	for (i = 0; i < DDIR_RWDIR_CNT; i++)
+		gs->min_bw[i] = gs->min_run[i] = ~0UL;
 }
 
 void init_thread_stat(struct thread_stat *ts)
@@ -924,7 +934,7 @@
 
 	memset(ts, 0, sizeof(*ts));
 
-	for (j = 0; j <= DDIR_WRITE; j++) {
+	for (j = 0; j < DDIR_RWDIR_CNT; j++) {
 		ts->lat_stat[j].min_val = -1UL;
 		ts->clat_stat[j].min_val = -1UL;
 		ts->slat_stat[j].min_val = -1UL;
@@ -1041,7 +1051,7 @@
 		rs = &runstats[ts->groupid];
 		rs->kb_base = ts->kb_base;
 
-		for (j = 0; j <= DDIR_WRITE; j++) {
+		for (j = 0; j < DDIR_RWDIR_CNT; j++) {
 			if (!ts->runtime[j])
 				continue;
 			if (ts->runtime[j] < rs->min_run[j] || !rs->min_run[j])
@@ -1067,12 +1077,15 @@
 	}
 
 	for (i = 0; i < groupid + 1; i++) {
+		int ddir;
+
 		rs = &runstats[i];
 
-		if (rs->max_run[0])
-			rs->agg[0] = (rs->io_kb[0] * 1000) / rs->max_run[0];
-		if (rs->max_run[1])
-			rs->agg[1] = (rs->io_kb[1] * 1000) / rs->max_run[1];
+		for (ddir = 0; ddir < DDIR_RWDIR_CNT; ddir++) {
+			if (rs->max_run[ddir])
+				rs->agg[ddir] = (rs->io_kb[ddir] * 1000) /
+						rs->max_run[ddir];
+		}
 	}
 
 	/*
@@ -1128,10 +1141,13 @@
 			td->ts.runtime[DDIR_READ] += rt[i];
 		if (td_write(td) && td->io_bytes[DDIR_WRITE])
 			td->ts.runtime[DDIR_WRITE] += rt[i];
+		if (td_trim(td) && td->io_bytes[DDIR_TRIM])
+			td->ts.runtime[DDIR_TRIM] += rt[i];
 
 		update_rusage_stat(td);
-		td->ts.io_bytes[0] = td->io_bytes[0];
-		td->ts.io_bytes[1] = td->io_bytes[1];
+		td->ts.io_bytes[DDIR_READ] = td->io_bytes[DDIR_READ];
+		td->ts.io_bytes[DDIR_WRITE] = td->io_bytes[DDIR_WRITE];
+		td->ts.io_bytes[DDIR_TRIM] = td->io_bytes[DDIR_TRIM];
 		td->ts.total_run_time = mtime_since(&td->epoch, &tv);
 	}
 
@@ -1142,6 +1158,8 @@
 			td->ts.runtime[DDIR_READ] -= rt[i];
 		if (td_write(td) && td->io_bytes[DDIR_WRITE])
 			td->ts.runtime[DDIR_WRITE] -= rt[i];
+		if (td_trim(td) && td->io_bytes[DDIR_TRIM])
+			td->ts.runtime[DDIR_TRIM] -= rt[i];
 	}
 
 	free(rt);
@@ -1259,9 +1277,17 @@
 		mw = iolog->avg_window[DDIR_WRITE].mean.u.f + 0.50;
 		__add_log_sample(iolog, mw, DDIR_WRITE, 0, elapsed);
 	}
+	if (iolog->avg_window[DDIR_TRIM].samples) {
+		unsigned long mw;
+
+		mw = iolog->avg_window[DDIR_TRIM].mean.u.f + 0.50;
+		__add_log_sample(iolog, mw, DDIR_TRIM, 0, elapsed);
+	}
+
 
 	reset_io_stat(&iolog->avg_window[DDIR_READ]);
 	reset_io_stat(&iolog->avg_window[DDIR_WRITE]);
+	reset_io_stat(&iolog->avg_window[DDIR_TRIM]);
 	iolog->avg_last = elapsed;
 }
 
@@ -1346,7 +1372,7 @@
 	/*
 	 * Compute both read and write rates for the interval.
 	 */
-	for (ddir = DDIR_READ; ddir <= DDIR_WRITE; ddir++) {
+	for (ddir = DDIR_READ; ddir < DDIR_RWDIR_CNT; ddir++) {
 		uint64_t delta;
 
 		delta = td->this_io_bytes[ddir] - td->stat_io_bytes[ddir];
@@ -1381,7 +1407,7 @@
 	/*
 	 * Compute both read and write rates for the interval.
 	 */
-	for (ddir = DDIR_READ; ddir <= DDIR_WRITE; ddir++) {
+	for (ddir = DDIR_READ; ddir < DDIR_RWDIR_CNT; ddir++) {
 		uint64_t delta;
 
 		delta = td->this_io_blocks[ddir] - td->stat_io_blocks[ddir];