18ee72638SRoi Dayan // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
28ee72638SRoi Dayan // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
38ee72638SRoi Dayan 
48ee72638SRoi Dayan #include <linux/if_vlan.h>
58ee72638SRoi Dayan #include "act.h"
68ee72638SRoi Dayan #include "vlan.h"
78ee72638SRoi Dayan #include "en/tc_priv.h"
88ee72638SRoi Dayan 
98ee72638SRoi Dayan static int
add_vlan_prio_tag_rewrite_action(struct mlx5e_priv * priv,struct mlx5e_tc_flow_parse_attr * parse_attr,u32 * action,struct netlink_ext_ack * extack)10c2208035SRoi Dayan add_vlan_prio_tag_rewrite_action(struct mlx5e_priv *priv,
11c2208035SRoi Dayan 				 struct mlx5e_tc_flow_parse_attr *parse_attr,
12c2208035SRoi Dayan 				 u32 *action, struct netlink_ext_ack *extack)
13c2208035SRoi Dayan {
14c2208035SRoi Dayan 	const struct flow_action_entry prio_tag_act = {
15c2208035SRoi Dayan 		.vlan.vid = 0,
16c2208035SRoi Dayan 		.vlan.prio =
17c2208035SRoi Dayan 			MLX5_GET(fte_match_set_lyr_2_4,
18c2208035SRoi Dayan 				 mlx5e_get_match_headers_value(*action,
19c2208035SRoi Dayan 							       &parse_attr->spec),
20c2208035SRoi Dayan 				 first_prio) &
21c2208035SRoi Dayan 			MLX5_GET(fte_match_set_lyr_2_4,
22c2208035SRoi Dayan 				 mlx5e_get_match_headers_criteria(*action,
23c2208035SRoi Dayan 								  &parse_attr->spec),
24c2208035SRoi Dayan 				 first_prio),
25c2208035SRoi Dayan 	};
26c2208035SRoi Dayan 
27c2208035SRoi Dayan 	return mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB,
2809bf9792SRoi Dayan 						    &prio_tag_act, parse_attr, action,
29c2208035SRoi Dayan 						    extack);
30c2208035SRoi Dayan }
31c2208035SRoi Dayan 
32c2208035SRoi Dayan static int
parse_tc_vlan_action(struct mlx5e_priv * priv,const struct flow_action_entry * act,struct mlx5_esw_flow_attr * attr,u32 * action,struct netlink_ext_ack * extack,struct mlx5e_tc_act_parse_state * parse_state)338ee72638SRoi Dayan parse_tc_vlan_action(struct mlx5e_priv *priv,
348ee72638SRoi Dayan 		     const struct flow_action_entry *act,
358ee72638SRoi Dayan 		     struct mlx5_esw_flow_attr *attr,
368ee72638SRoi Dayan 		     u32 *action,
37697319b2SMaor Dickman 		     struct netlink_ext_ack *extack,
38697319b2SMaor Dickman 		     struct mlx5e_tc_act_parse_state *parse_state)
398ee72638SRoi Dayan {
408ee72638SRoi Dayan 	u8 vlan_idx = attr->total_vlan;
418ee72638SRoi Dayan 
428ee72638SRoi Dayan 	if (vlan_idx >= MLX5_FS_VLAN_DEPTH) {
438ee72638SRoi Dayan 		NL_SET_ERR_MSG_MOD(extack, "Total vlans used is greater than supported");
448ee72638SRoi Dayan 		return -EOPNOTSUPP;
458ee72638SRoi Dayan 	}
468ee72638SRoi Dayan 
47633ad4b2SRoi Dayan 	if (!mlx5_eswitch_vlan_actions_supported(priv->mdev, vlan_idx)) {
48633ad4b2SRoi Dayan 		NL_SET_ERR_MSG_MOD(extack, "firmware vlan actions is not supported");
498ee72638SRoi Dayan 		return -EOPNOTSUPP;
508ee72638SRoi Dayan 	}
518ee72638SRoi Dayan 
52633ad4b2SRoi Dayan 	switch (act->id) {
53633ad4b2SRoi Dayan 	case FLOW_ACTION_VLAN_POP:
54633ad4b2SRoi Dayan 		if (vlan_idx)
558ee72638SRoi Dayan 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2;
56633ad4b2SRoi Dayan 		else
578ee72638SRoi Dayan 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
588ee72638SRoi Dayan 		break;
598ee72638SRoi Dayan 	case FLOW_ACTION_VLAN_PUSH:
608ee72638SRoi Dayan 		attr->vlan_vid[vlan_idx] = act->vlan.vid;
618ee72638SRoi Dayan 		attr->vlan_prio[vlan_idx] = act->vlan.prio;
628ee72638SRoi Dayan 		attr->vlan_proto[vlan_idx] = act->vlan.proto;
638ee72638SRoi Dayan 		if (!attr->vlan_proto[vlan_idx])
648ee72638SRoi Dayan 			attr->vlan_proto[vlan_idx] = htons(ETH_P_8021Q);
658ee72638SRoi Dayan 
66633ad4b2SRoi Dayan 		if (vlan_idx)
678ee72638SRoi Dayan 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2;
68633ad4b2SRoi Dayan 		else
698ee72638SRoi Dayan 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
708ee72638SRoi Dayan 		break;
71725726fdSMaor Dickman 	case FLOW_ACTION_VLAN_POP_ETH:
72725726fdSMaor Dickman 		parse_state->eth_pop = true;
73725726fdSMaor Dickman 		break;
74697319b2SMaor Dickman 	case FLOW_ACTION_VLAN_PUSH_ETH:
75697319b2SMaor Dickman 		if (!flow_flag_test(parse_state->flow, L3_TO_L2_DECAP))
76697319b2SMaor Dickman 			return -EOPNOTSUPP;
77697319b2SMaor Dickman 		parse_state->eth_push = true;
78697319b2SMaor Dickman 		memcpy(attr->eth.h_dest, act->vlan_push_eth.dst, ETH_ALEN);
79697319b2SMaor Dickman 		memcpy(attr->eth.h_source, act->vlan_push_eth.src, ETH_ALEN);
80697319b2SMaor Dickman 		break;
818ee72638SRoi Dayan 	default:
828ee72638SRoi Dayan 		NL_SET_ERR_MSG_MOD(extack, "Unexpected action id for VLAN");
838ee72638SRoi Dayan 		return -EINVAL;
848ee72638SRoi Dayan 	}
858ee72638SRoi Dayan 
868ee72638SRoi Dayan 	attr->total_vlan = vlan_idx + 1;
878ee72638SRoi Dayan 
888ee72638SRoi Dayan 	return 0;
898ee72638SRoi Dayan }
908ee72638SRoi Dayan 
918ee72638SRoi Dayan int
mlx5e_tc_act_vlan_add_push_action(struct mlx5e_priv * priv,struct mlx5_flow_attr * attr,struct net_device ** out_dev,struct netlink_ext_ack * extack)928ee72638SRoi Dayan mlx5e_tc_act_vlan_add_push_action(struct mlx5e_priv *priv,
938ee72638SRoi Dayan 				  struct mlx5_flow_attr *attr,
948ee72638SRoi Dayan 				  struct net_device **out_dev,
958ee72638SRoi Dayan 				  struct netlink_ext_ack *extack)
968ee72638SRoi Dayan {
978ee72638SRoi Dayan 	struct net_device *vlan_dev = *out_dev;
988ee72638SRoi Dayan 	struct flow_action_entry vlan_act = {
998ee72638SRoi Dayan 		.id = FLOW_ACTION_VLAN_PUSH,
1008ee72638SRoi Dayan 		.vlan.vid = vlan_dev_vlan_id(vlan_dev),
1018ee72638SRoi Dayan 		.vlan.proto = vlan_dev_vlan_proto(vlan_dev),
1028ee72638SRoi Dayan 		.vlan.prio = 0,
1038ee72638SRoi Dayan 	};
1048ee72638SRoi Dayan 	int err;
1058ee72638SRoi Dayan 
106697319b2SMaor Dickman 	err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action, extack, NULL);
1078ee72638SRoi Dayan 	if (err)
1088ee72638SRoi Dayan 		return err;
1098ee72638SRoi Dayan 
1108ee72638SRoi Dayan 	rcu_read_lock();
1118ee72638SRoi Dayan 	*out_dev = dev_get_by_index_rcu(dev_net(vlan_dev), dev_get_iflink(vlan_dev));
1128ee72638SRoi Dayan 	rcu_read_unlock();
1138ee72638SRoi Dayan 	if (!*out_dev)
1148ee72638SRoi Dayan 		return -ENODEV;
1158ee72638SRoi Dayan 
1168ee72638SRoi Dayan 	if (is_vlan_dev(*out_dev))
1178ee72638SRoi Dayan 		err = mlx5e_tc_act_vlan_add_push_action(priv, attr, out_dev, extack);
1188ee72638SRoi Dayan 
1198ee72638SRoi Dayan 	return err;
1208ee72638SRoi Dayan }
1218ee72638SRoi Dayan 
1228ee72638SRoi Dayan int
mlx5e_tc_act_vlan_add_pop_action(struct mlx5e_priv * priv,struct mlx5_flow_attr * attr,struct netlink_ext_ack * extack)1238ee72638SRoi Dayan mlx5e_tc_act_vlan_add_pop_action(struct mlx5e_priv *priv,
1248ee72638SRoi Dayan 				 struct mlx5_flow_attr *attr,
1258ee72638SRoi Dayan 				 struct netlink_ext_ack *extack)
1268ee72638SRoi Dayan {
1278ee72638SRoi Dayan 	struct flow_action_entry vlan_act = {
1288ee72638SRoi Dayan 		.id = FLOW_ACTION_VLAN_POP,
1298ee72638SRoi Dayan 	};
1308ee72638SRoi Dayan 	int nest_level, err = 0;
1318ee72638SRoi Dayan 
1328ee72638SRoi Dayan 	nest_level = attr->parse_attr->filter_dev->lower_level -
1338ee72638SRoi Dayan 						priv->netdev->lower_level;
1348ee72638SRoi Dayan 	while (nest_level--) {
1358ee72638SRoi Dayan 		err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action,
136697319b2SMaor Dickman 					   extack, NULL);
1378ee72638SRoi Dayan 		if (err)
1388ee72638SRoi Dayan 			return err;
1398ee72638SRoi Dayan 	}
1408ee72638SRoi Dayan 
1418ee72638SRoi Dayan 	return err;
1428ee72638SRoi Dayan }
1438ee72638SRoi Dayan 
1448ee72638SRoi Dayan static int
tc_act_parse_vlan(struct mlx5e_tc_act_parse_state * parse_state,const struct flow_action_entry * act,struct mlx5e_priv * priv,struct mlx5_flow_attr * attr)1458ee72638SRoi Dayan tc_act_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state,
1468ee72638SRoi Dayan 		  const struct flow_action_entry *act,
1478ee72638SRoi Dayan 		  struct mlx5e_priv *priv,
1488ee72638SRoi Dayan 		  struct mlx5_flow_attr *attr)
1498ee72638SRoi Dayan {
1508ee72638SRoi Dayan 	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
1518ee72638SRoi Dayan 	int err;
1528ee72638SRoi Dayan 
1538ee72638SRoi Dayan 	if (act->id == FLOW_ACTION_VLAN_PUSH &&
1548ee72638SRoi Dayan 	    (attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP)) {
1558ee72638SRoi Dayan 		/* Replace vlan pop+push with vlan modify */
1568ee72638SRoi Dayan 		attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
1578ee72638SRoi Dayan 		err = mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB, act,
15809bf9792SRoi Dayan 							   attr->parse_attr, &attr->action,
15909bf9792SRoi Dayan 							   parse_state->extack);
1608ee72638SRoi Dayan 	} else {
1618ee72638SRoi Dayan 		err = parse_tc_vlan_action(priv, act, esw_attr, &attr->action,
162697319b2SMaor Dickman 					   parse_state->extack, parse_state);
1638ee72638SRoi Dayan 	}
1648ee72638SRoi Dayan 
1658ee72638SRoi Dayan 	if (err)
1668ee72638SRoi Dayan 		return err;
1678ee72638SRoi Dayan 
1688ee72638SRoi Dayan 	esw_attr->split_count = esw_attr->out_count;
169*b7558a77SJianbo Liu 	parse_state->if_count = 0;
1708ee72638SRoi Dayan 
1718ee72638SRoi Dayan 	return 0;
1728ee72638SRoi Dayan }
1738ee72638SRoi Dayan 
174c2208035SRoi Dayan static int
tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state * parse_state,struct mlx5e_priv * priv,struct mlx5_flow_attr * attr)175c2208035SRoi Dayan tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state,
176c2208035SRoi Dayan 		       struct mlx5e_priv *priv,
177c2208035SRoi Dayan 		       struct mlx5_flow_attr *attr)
178c2208035SRoi Dayan {
179c2208035SRoi Dayan 	struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
180c2208035SRoi Dayan 	struct netlink_ext_ack *extack = parse_state->extack;
181c2208035SRoi Dayan 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
182c2208035SRoi Dayan 	int err;
183c2208035SRoi Dayan 
184c2208035SRoi Dayan 	if (MLX5_CAP_GEN(esw->dev, prio_tag_required) &&
185c2208035SRoi Dayan 	    attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) {
186c2208035SRoi Dayan 		/* For prio tag mode, replace vlan pop with rewrite vlan prio
187c2208035SRoi Dayan 		 * tag rewrite.
188c2208035SRoi Dayan 		 */
189c2208035SRoi Dayan 		attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
19009bf9792SRoi Dayan 		err = add_vlan_prio_tag_rewrite_action(priv, parse_attr,
191c2208035SRoi Dayan 						       &attr->action, extack);
192c2208035SRoi Dayan 		if (err)
193c2208035SRoi Dayan 			return err;
194c2208035SRoi Dayan 	}
195c2208035SRoi Dayan 
196c2208035SRoi Dayan 	return 0;
197c2208035SRoi Dayan }
198c2208035SRoi Dayan 
1998ee72638SRoi Dayan struct mlx5e_tc_act mlx5e_tc_act_vlan = {
2008ee72638SRoi Dayan 	.parse_action = tc_act_parse_vlan,
201c2208035SRoi Dayan 	.post_parse = tc_act_post_parse_vlan,
2028ee72638SRoi Dayan };
203