@@ -97,6 +97,8 @@ enum field_op_id {
FIELD_OP_PLUS,
FIELD_OP_MINUS,
FIELD_OP_UNARY_MINUS,
+ FIELD_OP_DIV,
+ FIELD_OP_MULT,
};
/*
@@ -285,6 +287,40 @@ static u64 hist_field_minus(struct hist_field *hist_field,
return val1 - val2;
}
+static u64 hist_field_div(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct trace_buffer *buffer,
+ struct ring_buffer_event *rbe,
+ void *event)
+{
+ struct hist_field *operand1 = hist_field->operands[0];
+ struct hist_field *operand2 = hist_field->operands[1];
+
+ u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
+ u64 val2 = operand2->fn(operand2, elt, buffer, rbe, event);
+
+ /* Return -1 for the undefined case */
+ if (!val2)
+ return -1;
+
+ return div64_u64(val1, val2);
+}
+
+static u64 hist_field_mult(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct trace_buffer *buffer,
+ struct ring_buffer_event *rbe,
+ void *event)
+{
+ struct hist_field *operand1 = hist_field->operands[0];
+ struct hist_field *operand2 = hist_field->operands[1];
+
+ u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
+ u64 val2 = operand2->fn(operand2, elt, buffer, rbe, event);
+
+ return val1 * val2;
+}
+
static u64 hist_field_unary_minus(struct hist_field *hist_field,
struct tracing_map_elt *elt,
struct trace_buffer *buffer,
@@ -1592,6 +1628,12 @@ static char *expr_str(struct hist_field *field, unsigned int level)
case FIELD_OP_PLUS:
strcat(expr, "+");
break;
+ case FIELD_OP_DIV:
+ strcat(expr, "/");
+ break;
+ case FIELD_OP_MULT:
+ strcat(expr, "*");
+ break;
default:
kfree(expr);
return NULL;
@@ -1607,7 +1649,7 @@ static int contains_operator(char *str)
enum field_op_id field_op = FIELD_OP_NONE;
char *op;
- op = strpbrk(str, "+-");
+ op = strpbrk(str, "+-/*");
if (!op)
return FIELD_OP_NONE;
@@ -1628,6 +1670,12 @@ static int contains_operator(char *str)
case '+':
field_op = FIELD_OP_PLUS;
break;
+ case '/':
+ field_op = FIELD_OP_DIV;
+ break;
+ case '*':
+ field_op = FIELD_OP_MULT;
+ break;
default:
break;
}
@@ -2361,10 +2409,26 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
case FIELD_OP_PLUS:
sep = "+";
break;
+ case FIELD_OP_DIV:
+ sep = "/";
+ break;
+ case FIELD_OP_MULT:
+ sep = "*";
+ break;
default:
goto free;
}
+ /*
+ * Multiplication and division are only supported in single operator
+ * expressions, since the expression is always evaluated from right
+ * to left.
+ */
+ if ((field_op == FIELD_OP_DIV || field_op == FIELD_OP_MULT) && level > 0) {
+ hist_err(file->tr, HIST_ERR_TOO_MANY_SUBEXPR, errpos(str));
+ return ERR_PTR(-EINVAL);
+ }
+
operand1_str = strsep(&str, sep);
if (!operand1_str || !str)
goto free;
@@ -2436,6 +2500,12 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
case FIELD_OP_PLUS:
expr->fn = hist_field_plus;
break;
+ case FIELD_OP_DIV:
+ expr->fn = hist_field_div;
+ break;
+ case FIELD_OP_MULT:
+ expr->fn = hist_field_mult;
+ break;
default:
ret = -EINVAL;
goto free;