Commit 392d0ab0 authored by Steen Hegelund's avatar Steen Hegelund Committed by David S. Miller
Browse files

net: microchip: sparx5: Adding TC goto action and action checking



Add support for a goto action and ensure that a HW offloaded TC flower
filter has a valid goto action and that pass and trap actions are not both
used in the same filter.

Signed-off-by: default avatarSteen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7de1dcad
Loading
Loading
Loading
Loading
+62 −8
Original line number Diff line number Diff line
@@ -464,6 +464,60 @@ static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
	return err;
}

static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
					 struct flow_cls_offload *fco,
					 struct vcap_admin *admin)
{
	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
	struct flow_action_entry *actent, *last_actent = NULL;
	struct flow_action *act = &rule->action;
	u64 action_mask = 0;
	int idx;

	if (!flow_action_has_entries(act)) {
		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
		return -EINVAL;
	}

	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
		return -EOPNOTSUPP;

	flow_action_for_each(idx, actent, act) {
		if (action_mask & BIT(actent->id)) {
			NL_SET_ERR_MSG_MOD(fco->common.extack,
					   "More actions of the same type");
			return -EINVAL;
		}
		action_mask |= BIT(actent->id);
		last_actent = actent; /* Save last action for later check */
	}

	/* Check that last action is a goto */
	if (last_actent->id != FLOW_ACTION_GOTO) {
		NL_SET_ERR_MSG_MOD(fco->common.extack,
				   "Last action must be 'goto'");
		return -EINVAL;
	}

	/* Check if the goto chain is in the next lookup */
	if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
				 last_actent->chain_index)) {
		NL_SET_ERR_MSG_MOD(fco->common.extack,
				   "Invalid goto chain");
		return -EINVAL;
	}

	/* Catch unsupported combinations of actions */
	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
		NL_SET_ERR_MSG_MOD(fco->common.extack,
				   "Cannot combine pass and trap action");
		return -EOPNOTSUPP;
	}

	return 0;
}

static int sparx5_tc_flower_replace(struct net_device *ndev,
				    struct flow_cls_offload *fco,
				    struct vcap_admin *admin)
@@ -475,16 +529,12 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
	struct vcap_rule *vrule;
	int err, idx;

	frule = flow_cls_offload_flow_rule(fco);
	if (!flow_action_has_entries(&frule->action)) {
		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
		return -EINVAL;
	}
	vctrl = port->sparx5->vcap_ctrl;

	if (!flow_action_basic_hw_stats_check(&frule->action, fco->common.extack))
		return -EOPNOTSUPP;
	err = sparx5_tc_flower_action_check(vctrl, fco, admin);
	if (err)
		return err;

	vctrl = port->sparx5->vcap_ctrl;
	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,
				fco->common.prio, 0);
	if (IS_ERR(vrule))
@@ -492,6 +542,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,

	vrule->cookie = fco->cookie;
	sparx5_tc_use_dissectors(fco, admin, vrule);
	frule = flow_cls_offload_flow_rule(fco);
	flow_action_for_each(idx, act, &frule->action) {
		switch (act->id) {
		case FLOW_ACTION_TRAP:
@@ -521,6 +572,9 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
			if (err)
				goto out;
			break;
		case FLOW_ACTION_GOTO:
			/* Links between VCAPs will be added later */
			break;
		default:
			NL_SET_ERR_MSG_MOD(fco->common.extack,
					   "Unsupported TC action");
+36 −0
Original line number Diff line number Diff line
@@ -677,6 +677,42 @@ struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid)
}
EXPORT_SYMBOL_GPL(vcap_find_admin);

/* Is the next chain id in the following lookup, possible in another VCAP */
bool vcap_is_next_lookup(struct vcap_control *vctrl, int cur_cid, int next_cid)
{
	struct vcap_admin *admin, *next_admin;
	int lookup, next_lookup;

	/* The offset must be at least one lookup */
	if (next_cid < cur_cid + VCAP_CID_LOOKUP_SIZE)
		return false;

	if (vcap_api_check(vctrl))
		return false;

	admin = vcap_find_admin(vctrl, cur_cid);
	if (!admin)
		return false;

	/* If no VCAP contains the next chain, the next chain must be beyond
	 * the last chain in the current VCAP
	 */
	next_admin = vcap_find_admin(vctrl, next_cid);
	if (!next_admin)
		return next_cid > admin->last_cid;

	lookup = vcap_chain_id_to_lookup(admin, cur_cid);
	next_lookup = vcap_chain_id_to_lookup(next_admin, next_cid);

	/* Next lookup must be the following lookup */
	if (admin == next_admin || admin->vtype == next_admin->vtype)
		return next_lookup == lookup + 1;

	/* Must be the first lookup in the next VCAP instance */
	return next_lookup == 0;
}
EXPORT_SYMBOL_GPL(vcap_is_next_lookup);

/* Check if there is room for a new rule */
static int vcap_rule_space(struct vcap_admin *admin, int size)
{
+2 −0
Original line number Diff line number Diff line
@@ -193,6 +193,8 @@ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule,
					      enum vcap_key_field key);
/* Find a rule id with a provided cookie */
int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie);
/* Is the next chain id in the following lookup, possible in another VCAP */
bool vcap_is_next_lookup(struct vcap_control *vctrl, int cur_cid, int next_cid);

/* Copy to host byte order */
void vcap_netbytes_copy(u8 *dst, u8 *src, int count);