Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d6617258 authored by Girish Mahadevan's avatar Girish Mahadevan Committed by Kiran Gunda
Browse files

msm: msm_bus: Add support for multiple ops in rules engine



Rules engine enables implementing SoC level workaround for system
performance issues allowing users to define these workaround rules.
Currently the rules only allow homogenous comparison operations against
a singular threshold. Expanding this to allow heterogenous comparison
operations against multiple thresholds thus allowing definition of more
complicated rules.

Change-Id: I51a31376e8368931552aecfc82e6cc43e4b18b1e
Signed-off-by: default avatarGirish Mahadevan <girishm@codeaurora.org>
Signed-off-by: default avatarKiran Gunda <kgunda@codeaurora.org>
parent e12c33f7
Loading
Loading
Loading
Loading
+18 −13
Original line number Diff line number Diff line
@@ -16,17 +16,18 @@ conditions to be monitored. The mandatory properties for the rules are

- qcom,src-nodes:		An array of phandles denoting the source nodes
				whose bandwidth votes need to be monitored.
- qcom,src-field:		This field represents the voted field of the
				source node to be monitored. Possible values
				are FLD_IB/FLD_AB/FLD_CLK
- qcom,src-op:			The operand to be used when evaluating a node's
				bandwidth vote with a threshold.Possible values
				are OP_LE/OP_LT/OP_GT/OP_GE.
- qcom,thresh:			The threshold in Kbytes/s to be used in vote
				evaluation.
- qcom,src-field:		An array of fields represents the voted field
				of the source node to be monitored. Possible
				values are FLD_IB/FLD_AB/FLD_CLK.
- qcom,src-op:			An array of operands to be used when evaluating
				a node's bandwidth vote with a threshold.
				Possible values are OP_LE/OP_LT/OP_GT/OP_GE.
- qcom,thresh:			An array of thresholds in Kbytes/s
				(in FLD_IB/FLD_AB case) or KHz (in FLD_CLK case)
				to be used in vote evaluation.
- qcom,mode:			The QoS mode to be applied when this rule's
				criterion are satisfied. Possible values are
				THROTTLE_ON/THROTTLE_OFF
				THROTTLE_ON/THROTTLE_OFF.
- qcom,dest-node:		An array of phandles representing the nodes to
				which the QoS mode is to be applied.

@@ -34,6 +35,9 @@ The optional properties for the rule node are:
- qcom,dest-bw:			The destination bandwidth value in Kbytes/s to
				be used toward the QoS mode for the destination
				node.
- qcom,combo-op:		A property that is only required when there
				are multiple comparison operands to multiple
				thresholds, can be OP_AND or OP_OR.

Example:
	static-rules {
@@ -42,13 +46,14 @@ Example:
		#size-cells = <0>;

		rule@0 {
			qcom,src-nodes = <&mas_apss>;
			qcom,src-field = <FLD_IB>;
			qcom,src-op = <OP_LE>;
			qcom,thresh = <1599078>;
			qcom,src-nodes = <&mas_apss &mas_mdp>;
			qcom,src-field = <FLD_IB FLD_AB>;
			qcom,src-op = <OP_LE OP_GT>;
			qcom,thresh = <1599078 0>;
			qcom,mode = <THROTTLE_ON>;
			qcom,dest-node = <&mas_apss>;
			qcom,dest-bw = <1599078>;
			qcom,combo-op = <OP_AND>;
		};

		rule@1 {
+69 −20
Original line number Diff line number Diff line
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -808,6 +808,9 @@ int msm_bus_of_get_static_rules(struct platform_device *pdev,
	int bw_fld = 0;
	int i;
	struct bus_rule_type *local_rule = NULL;
	int *thresh_arr = NULL;
	int num_op = 0;
	int num_fld = 0;

	of_node = pdev->dev.of_node;
	num_rules = of_get_child_count(of_node);
@@ -832,37 +835,58 @@ int msm_bus_of_get_static_rules(struct platform_device *pdev,
			&local_rule[rule_idx].num_dst,
			"qcom,dest-node");

		ret = of_property_read_u32(child_node, "qcom,src-field",
				&local_rule[rule_idx].src_field);
		if (ret) {
			dev_err(&pdev->dev, "src-field missing");
		if (local_rule[rule_idx].num_dst > 1) {
			dev_err(&pdev->dev, "Only 1 dest node supported\n");
			ret = -ENXIO;
			goto err_static_rules;
		}

		ret = of_property_read_u32(child_node, "qcom,src-op",
				&local_rule[rule_idx].op);
		if (ret) {
			dev_err(&pdev->dev, "src-op missing");
		thresh_arr = get_arr(pdev, child_node,
				"qcom,thresh",
				&local_rule[rule_idx].num_thresh);

		if (!local_rule[rule_idx].num_thresh ||
				((local_rule[rule_idx].num_thresh > 1) &&
				 local_rule[rule_idx].num_thresh !=
				 local_rule[rule_idx].num_src)) {
			dev_err(&pdev->dev, "thresholds missing");
			ret = -ENXIO;
			goto err_static_rules;
		} else {
			int i;

			local_rule[rule_idx].thresh =
				devm_kzalloc(&pdev->dev,
				sizeof(u64) * local_rule[rule_idx].num_thresh,
				GFP_KERNEL);
			if (!IS_ERR_OR_NULL(thresh_arr)) {
				for (i = 0;
				    i < local_rule[rule_idx].num_thresh; ++i)
					local_rule[rule_idx].thresh[i] =
						KBTOB(thresh_arr[i]);
			}
		}

		ret = of_property_read_u32(child_node, "qcom,mode",
				&local_rule[rule_idx].mode);
		if (ret) {
			dev_err(&pdev->dev, "mode missing");
		local_rule[rule_idx].src_field = get_arr(pdev, child_node,
				"qcom,src-field", &num_fld);
		if (!num_fld || (num_fld != local_rule[rule_idx].num_thresh)) {
			dev_err(&pdev->dev, "src-field missing");
			ret = -ENXIO;
			goto err_static_rules;
		}

		ret = of_property_read_u32(child_node, "qcom,thresh", &bw_fld);
		if (ret) {
			dev_err(&pdev->dev, "thresh missing");
		local_rule[rule_idx].op = get_arr(pdev, child_node,
				"qcom,src-op", &num_op);
		if (!num_op || (num_op != local_rule[rule_idx].num_thresh)) {
			dev_err(&pdev->dev, "src-op missing");
			ret = -ENXIO;
			goto err_static_rules;
		} else
			local_rule[rule_idx].thresh = KBTOB(bw_fld);
		}

		ret = of_property_read_u32(child_node, "qcom,mode",
				&local_rule[rule_idx].mode);
		if (ret)
			local_rule[rule_idx].mode = THROTTLE_OFF;

		ret = of_property_read_u32(child_node, "qcom,dest-bw",
								&bw_fld);
@@ -871,9 +895,21 @@ int msm_bus_of_get_static_rules(struct platform_device *pdev,
		else
			local_rule[rule_idx].dst_bw = KBTOB(bw_fld);

		rule_idx++;
		ret = of_property_read_u32(child_node, "qcom,combo-op",
				&local_rule[rule_idx].combo_op);
		if (ret) {
			if (local_rule[rule_idx].num_thresh > 1) {
				dev_err(&pdev->dev, "combo-op missing");
				ret = -ENXIO;
				goto err_static_rules;
			} else
				local_rule[rule_idx].combo_op = 0;
		}

		++rule_idx;
	}
	ret = rule_idx;

exit_static_rules:
	return ret;
err_static_rules:
@@ -885,9 +921,22 @@ err_static_rules:
			if (!IS_ERR_OR_NULL(local_rule[i].dst_node))
				devm_kfree(&pdev->dev,
						local_rule[i].dst_node);
			devm_kfree(&pdev->dev, local_rule);
			if (!IS_ERR_OR_NULL(thresh_arr))
				devm_kfree(&pdev->dev, thresh_arr);
			if (!IS_ERR_OR_NULL(local_rule[i].thresh))
				devm_kfree(&pdev->dev,
						local_rule[i].thresh);
			if (!IS_ERR_OR_NULL(local_rule[i].op))
				devm_kfree(&pdev->dev,
						local_rule[i].op);
			if (!IS_ERR_OR_NULL(local_rule[i].src_field))
				devm_kfree(&pdev->dev,
						local_rule[i].src_field);
			if (!IS_ERR_OR_NULL(thresh_arr))
				devm_kfree(&pdev->dev, thresh_arr);
		}
	}
	devm_kfree(&pdev->dev, local_rule);
	*static_rules = NULL;
	return ret;
}
+114 −77
Original line number Diff line number Diff line
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -128,9 +128,6 @@ static bool do_compare_op(u64 op1, u64 op2, int op)
	case OP_GE:
		ret = GE(op1, op2);
		break;
	case OP_NOOP:
		ret = true;
		break;
	default:
		pr_info("Invalid OP %d", op);
		break;
@@ -155,13 +152,13 @@ static void update_src_id_vote(struct rule_update_path_info *inp_node,
	}
}

static u64 get_field(struct rules_def *rule, int src_id)
static u64 get_sum_field(struct rules_def *rule)
{
	u64 field = 0;
	int i;

	for (i = 0; i < rule->num_src; i++) {
		switch (rule->rule_ops.src_field) {
		switch (rule->rule_ops.src_field[0]) {
		case FLD_IB:
			field += rule->src_info[i].ib;
			break;
@@ -177,28 +174,58 @@ static u64 get_field(struct rules_def *rule, int src_id)
	return field;
}

static bool check_rule(struct rules_def *rule,
			struct rule_update_path_info *inp)
static u64 get_field(struct rules_def *rule, int src_id)
{
	u64 field = 0;
	int i;

	for (i = 0; i < rule->num_src; i++) {
		if (rule->src_info[i].id == src_id) {
			switch (rule->rule_ops.src_field[i]) {
			case FLD_IB:
				field = rule->src_info[i].ib;
				break;
			case FLD_AB:
				field = rule->src_info[i].ab;
				break;
			case FLD_CLK:
				field = rule->src_info[i].clk;
				break;
			}
			break;
		}
	}
	return field;
}

static bool check_rule(struct rules_def *rule)
{
	bool ret = false;
	u64 src_field = 0;
	int i;

	if (!rule)
		return ret;

	switch (rule->rule_ops.op) {
	case OP_LE:
	case OP_LT:
	case OP_GT:
	case OP_GE:
	{
		u64 src_field = get_field(rule, inp->id);
		ret = do_compare_op(src_field, rule->rule_ops.thresh,
							rule->rule_ops.op);
		break;
	for (i = 0; i < rule->rule_ops.num_thresh; i++) {
		if (rule->rule_ops.op[i] > OP_GT) {
			pr_err("Unsupported op %d", rule->rule_ops.op[i]);
			continue;
		}
		if (rule->rule_ops.num_thresh > 1)
			src_field = get_field(rule, rule->src_info[i].id);
		else
			src_field = get_sum_field(rule);

		ret = do_compare_op(src_field, rule->rule_ops.thresh[i],
						rule->rule_ops.op[i]);
		if (rule->rule_ops.combo_op == OP_AND) {
			if (!ret)
				return ret;
		} else if (rule->rule_ops.combo_op == OP_OR) {
			if (ret)
				return ret;
		}
	default:
		pr_err("Unsupported op %d", rule->rule_ops.op);
		break;
	}
	return ret;
}
@@ -212,7 +239,7 @@ static void match_rule(struct rule_update_path_info *inp_node,
	list_for_each_entry(rule, &node->node_rules, link) {
		for (i = 0; i < rule->num_src; i++) {
			if (rule->src_info[i].id == inp_node->id) {
				if (check_rule(rule, inp_node)) {
				if (check_rule(rule)) {
					trace_bus_rules_matches(
						(node->cur_rule ?
						 node->cur_rule->rule_id : -1),
@@ -298,33 +325,38 @@ int msm_rules_update_path(struct list_head *input_list,
	return ret;
}

static bool ops_equal(int op1, int op2)
static bool is_throttle_rule(int mode)
{
	bool ret = false;
	bool ret = true;

	switch (op1) {
	case OP_GT:
	case OP_GE:
	case OP_LT:
	case OP_LE:
		if (abs(op1 - op2) <= 1)
			ret = true;
		break;
	default:
		ret = (op1 == op2);
	}
	if (mode == THROTTLE_OFF)
		ret = false;

	return ret;
}

static bool is_throttle_rule(int mode)
static int64_t get_th_diff(struct rules_def *ra, struct rules_def *rb)
{
	bool ret = true;
	int64_t th_diff = 0;
	int num_thresh = 1;

	if (mode == THROTTLE_OFF)
		ret = false;
	if (!(ra && rb))
		return -ENXIO;

	return ret;
	num_thresh = ra->rule_ops.num_thresh;
	if (num_thresh > 1) {
		th_diff = ra->rule_ops.dst_bw -
					rb->rule_ops.dst_bw;
	} else {
		if ((ra->rule_ops.op[0] == OP_LE) ||
				(ra->rule_ops.op[0] == OP_LT))
			th_diff = ra->rule_ops.thresh[0] -
					rb->rule_ops.thresh[0];
		else
			th_diff = rb->rule_ops.thresh[0] -
					ra->rule_ops.thresh[0];
	}
	return th_diff;
}

static int node_rules_compare(void *priv, struct list_head *a,
@@ -337,26 +369,17 @@ static int node_rules_compare(void *priv, struct list_head *a,


	if (ra->rule_ops.mode == rb->rule_ops.mode) {
		if (ops_equal(ra->rule_ops.op, rb->rule_ops.op)) {
			if ((ra->rule_ops.op == OP_LT) ||
				(ra->rule_ops.op == OP_LE)) {
				th_diff = ra->rule_ops.thresh -
						rb->rule_ops.thresh;
				if (th_diff > 0)
					ret = 1;
				 else
					ret = -1;
			} else if ((ra->rule_ops.op == OP_GT) ||
					(ra->rule_ops.op == OP_GE)) {
				th_diff = rb->rule_ops.thresh -
							ra->rule_ops.thresh;
		if ((ra->rule_ops.num_thresh == 1) &&
			(ra->rule_ops.op[0] - rb->rule_ops.op[0]))
			ret = ra->rule_ops.op - rb->rule_ops.op;
		else {
			th_diff = get_th_diff(ra, rb);

			if (th_diff > 0)
				ret = 1;
			 else
				ret = -1;
		}
		} else
			ret = ra->rule_ops.op - rb->rule_ops.op;
	} else if (is_throttle_rule(ra->rule_ops.mode) &&
				is_throttle_rule(rb->rule_ops.mode)) {
		if (ra->rule_ops.mode == THROTTLE_ON)
@@ -390,7 +413,8 @@ static void print_rules(struct rule_node_info *node_it)
	list_for_each_entry(node_rule, &node_it->node_rules, link) {
		pr_info("\n num Rules %d  rule Id %d\n",
				node_it->num_rules, node_rule->rule_id);
		pr_info("Rule: src_field %d\n", node_rule->rule_ops.src_field);
		pr_info("Rule: src_field %d\n",
					node_rule->rule_ops.src_field[0]);
		for (i = 0; i < node_rule->rule_ops.num_src; i++)
			pr_info("Rule: src %d\n",
					node_rule->rule_ops.src_id[i]);
@@ -399,8 +423,8 @@ static void print_rules(struct rule_node_info *node_it)
						node_rule->rule_ops.dst_node[i],
						node_rule->rule_ops.dst_bw);
		pr_info("Rule: thresh %llu op %d mode %d State %d\n",
					node_rule->rule_ops.thresh,
					node_rule->rule_ops.op,
					node_rule->rule_ops.thresh[0],
					node_rule->rule_ops.op[0],
					node_rule->rule_ops.mode,
					node_rule->state);
	}
@@ -431,25 +455,31 @@ void print_rules_buf(char *buf, int max_buf)
				"\nNum Rules:%d ruleId %d STATE:%d change:%d\n",
				node_it->num_rules, node_rule->rule_id,
				node_rule->state, node_rule->state_change);
			for (i = 0; i < node_rule->rule_ops.num_thresh; i++)
				cnt += scnprintf(buf + cnt, max_buf - cnt,
					"Src_field %d\n",
				node_rule->rule_ops.src_field);
					node_rule->rule_ops.src_field[i]);
			for (i = 0; i < node_rule->rule_ops.num_src; i++)
				cnt += scnprintf(buf + cnt, max_buf - cnt,
					"Src %d Cur Ib %llu Ab %llu\n",
					"Src %d Cur Ib %llu Ab %llu Clk %llu\n",
					node_rule->rule_ops.src_id[i],
					node_rule->src_info[i].ib,
					node_rule->src_info[i].ab);
					node_rule->src_info[i].ab,
					node_rule->src_info[i].clk);
			for (i = 0; i < node_rule->rule_ops.num_dst; i++)
				cnt += scnprintf(buf + cnt, max_buf - cnt,
					"Dst %d dst_bw %llu\n",
					node_rule->rule_ops.dst_node[0],
					node_rule->rule_ops.dst_bw);
			for (i = 0; i < node_rule->rule_ops.num_thresh; i++)
				cnt += scnprintf(buf + cnt, max_buf - cnt,
					"Thresh %llu op %d mode %d\n",
					node_rule->rule_ops.thresh,
					node_rule->rule_ops.op,
					node_rule->rule_ops.thresh[i],
					node_rule->rule_ops.op[i],
					node_rule->rule_ops.mode);
			scnprintf(buf+cnt, max_buf - cnt,
					"Combo Op %d\n",
					node_rule->rule_ops.combo_op);
		}
	}
}
@@ -573,9 +603,16 @@ static int comp_rules(struct bus_rule_type *rulea, struct bus_rule_type *ruleb)
	if (!ret && (rulea->num_dst == ruleb->num_dst))
		ret = memcmp(rulea->dst_node, ruleb->dst_node,
				(sizeof(int) * rulea->num_dst));
	if (ret || (rulea->dst_bw != ruleb->dst_bw) ||
		(rulea->op != ruleb->op) || (rulea->thresh != ruleb->thresh))
	if (!ret && (rulea->num_thresh == ruleb->num_thresh))
		ret = (memcmp(rulea->op, ruleb->op,
				(sizeof(int) * rulea->num_thresh)) &&
			memcmp(rulea->thresh, ruleb->thresh,
				(sizeof(int) * rulea->num_thresh)) &&
			memcmp(rulea->src_field, ruleb->src_field,
				(sizeof(int) * rulea->num_thresh)));
	if (ret || (rulea->dst_bw != ruleb->dst_bw))
		ret = 1;

	return ret;
}

+4 −2
Original line number Diff line number Diff line
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -22,6 +22,8 @@
#define OP_GE	2
#define OP_GT	3
#define OP_NOOP 4
#define OP_AND	5
#define OP_OR	6

#define RULE_STATE_NOT_APPLIED	0
#define RULE_STATE_APPLIED	1
+6 −4
Original line number Diff line number Diff line
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -40,9 +40,11 @@ struct rule_apply_rcm_info {
struct bus_rule_type {
	int num_src;
	int *src_id;
	int src_field;
	int op;
	u64 thresh;
	int *src_field;
	int *op;
	int combo_op;
	int num_thresh;
	u64 *thresh;
	int num_dst;
	int *dst_node;
	u64 dst_bw;