fio: support suffixes in expression parser

Note that time values in expressions by default have units of microseconds

Signed-off-by: Stephen M. Cameron <stephenmcameron@gmail.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
diff --git a/exp/expression-parser.l b/exp/expression-parser.l
index 388515e..f688da1 100644
--- a/exp/expression-parser.l
+++ b/exp/expression-parser.l
@@ -36,12 +36,78 @@
 static void __attribute__((unused)) yyunput(int c,char *buf_ptr);
 static int __attribute__((unused)) input(void);
 
+#define set_suffix_value(yylval, i_val, d_val, has_d_val) \
+	(yylval).v.dval = (d_val); \
+	(yylval).v.ival = (i_val); \
+	(yylval).v.has_dval = (has_d_val); \
+	(yylval).v.has_error = 0;
+
 %}
 
 %%
 
 
-bye	return BYE;
+bye		return BYE;
+[kK]|[kK][bB] 	{
+			set_suffix_value(yylval, 1024, 1024.0, 0);
+			return SUFFIX;
+		}
+[Mm]|[Mm][bB]	{
+			set_suffix_value(yylval, 1024 * 1024, 1024.0 * 1024.0, 0);
+			return SUFFIX;
+		}
+[mM][sS]	{
+			set_suffix_value(yylval, 1000, 1000.0, 1);
+			return SUFFIX;
+		}
+[uU][sS]	{
+			set_suffix_value(yylval, 1, 1.0, 1);
+			return SUFFIX;
+		}
+[gG]|[Gg][Bb]	{
+			set_suffix_value(yylval, 1024LL * 1024 * 1024, 1024.0 * 1024.0 * 1024, 0);
+			return SUFFIX;
+		}
+[tT]|[tT][bB]	{	
+			set_suffix_value(yylval, 1024LL * 1024 * 1024 * 1024,
+						1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024, 0);
+			return SUFFIX;
+		}
+[pP]|[pP][bB]	{	
+			set_suffix_value(yylval, 1024LL * 1024 * 1024 * 1024 * 1024,
+					1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0, 0);
+			return SUFFIX;
+		}
+[kK][iI][Bb]	{
+			set_suffix_value(yylval, 1000LL, 1000.0, 0);
+			return SUFFIX;
+		}
+[mM][Ii][bB]	{
+			set_suffix_value(yylval, 1000000LL, 1000000.0 , 0);
+			return SUFFIX;
+		}
+[gG][iI][Bb]	{
+			set_suffix_value(yylval, 1000000000LL, 1000000000.0 , 0);
+			return SUFFIX;
+		}
+[pP][iI][Bb]	{	
+			set_suffix_value(yylval, 1000000000000LL, 1000000000000.0 , 0);
+			return SUFFIX;
+		}
+[sS]		{
+			set_suffix_value(yylval, 1000000LL, 1000000.0 , 0);
+			return SUFFIX;
+		}
+[dD]		{
+			set_suffix_value(yylval, 60LL * 60LL * 24LL * 1000000LL,
+						60.0 * 60.0 * 24.0 * 1000000.0, 0);
+			return SUFFIX;
+		}
+[hH]		{	
+			set_suffix_value(yylval, 60LL * 60LL * 1000000LL,
+					60.0 * 60.0 * 1000000.0, 0);
+			return SUFFIX;
+		}
 [ \t] ; /* ignore whitespace */
 #.+ ; /* ignore comments */
 [0-9]*[.][0-9]+ {
diff --git a/exp/expression-parser.y b/exp/expression-parser.y
index dbd8b6c..1c1ebf8 100644
--- a/exp/expression-parser.y
+++ b/exp/expression-parser.y
@@ -55,6 +55,7 @@
 
 %token <v> NUMBER
 %token <v> BYE
+%token <v> SUFFIX 
 %left '-' '+'
 %left '*' '/'
 %nonassoc UMINUS
@@ -119,6 +120,17 @@
 			$$.has_error = $2.has_error;
 		}
 	|	'(' expression ')' { $$ = $2; }
+	|	expression SUFFIX {
+			if (!$1.has_dval && !$2.has_dval)
+				$$.ival = $1.ival * $2.ival;
+			else
+				$$.ival = (long long) $1.dval * $2.dval;
+			if ($1.has_dval || $2.has_dval)
+				$$.dval = $1.dval * $2.dval;
+			else
+				$$.dval = $1.ival * $2.ival;
+			$$.has_error = $1.has_error || $2.has_error;
+		}
 	|	NUMBER { $$ = $1; }
 	|	BYE { $$ = $1; *bye = 1; };
 %%
diff --git a/exp/test-expression-parser.c b/exp/test-expression-parser.c
index 6b4ab5d..a9794dc 100644
--- a/exp/test-expression-parser.c
+++ b/exp/test-expression-parser.c
@@ -39,7 +39,7 @@
 			buffer[rc - 1] = '\0';
 		rc = evaluate_arithmetic_expression(buffer, &result, &dresult);
 		if (!rc) {
-			printf("%lld (%lf)\n", result, dresult);
+			printf("%lld (%20.20lf)\n", result, dresult);
 		} else {
 			result = 0;
 			dresult = 0;
diff --git a/fio.1 b/fio.1
index 2fbdb00..fb5ca46 100644
--- a/fio.1
+++ b/fio.1
@@ -117,9 +117,12 @@
 .SS Types
 Some parameters may take arguments of a specific type.
 Anywhere a numeric value is required, an arithmetic expression may be used,
-provided it is surrounded by parentheses.  Suffixes currently do not
-work with arithmetic expressions.  Supported operators are
+provided it is surrounded by parentheses.
+Supported operators are
 addition, subtraction, multiplication and division.
+For time values in expressions
+units are microseconds by default.  This is different than for time
+values not in expressions (not enclosed in parentheses).
 The types used are:
 .TP
 .I str
diff --git a/parse.c b/parse.c
index c2d1cc8..b632bf1 100644
--- a/parse.c
+++ b/parse.c
@@ -312,8 +312,12 @@
 #ifdef CONFIG_ARITHMETIC
 	if (str[0] == '(')
 		rc = evaluate_arithmetic_expression(str, &ival, &dval);
-	if (str[0] == '(' && !rc)
-		*val = ival;
+	if (str[0] == '(' && !rc) {
+		if (!kilo && is_seconds)
+			*val = ival / 1000000LL;
+		else
+			*val = ival;
+	}
 #endif
 
 	if (rc == 1) {