xref: /linux/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.c (revision 8be4d31cb8aaeea27bde4b7ddb26e28a89062ebf)
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 
4 #include <linux/if_vlan.h>
5 #include "act.h"
6 #include "vlan.h"
7 #include "en/tc_priv.h"
8 
9 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)10 add_vlan_prio_tag_rewrite_action(struct mlx5e_priv *priv,
11 				 struct mlx5e_tc_flow_parse_attr *parse_attr,
12 				 u32 *action, struct netlink_ext_ack *extack)
13 {
14 	const struct flow_action_entry prio_tag_act = {
15 		.vlan.vid = 0,
16 		.vlan.prio =
17 			MLX5_GET(fte_match_set_lyr_2_4,
18 				 mlx5e_get_match_headers_value(*action,
19 							       &parse_attr->spec),
20 				 first_prio) &
21 			MLX5_GET(fte_match_set_lyr_2_4,
22 				 mlx5e_get_match_headers_criteria(*action,
23 								  &parse_attr->spec),
24 				 first_prio),
25 	};
26 
27 	return mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB,
28 						    &prio_tag_act, parse_attr, action,
29 						    extack);
30 }
31 
32 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)33 parse_tc_vlan_action(struct mlx5e_priv *priv,
34 		     const struct flow_action_entry *act,
35 		     struct mlx5_esw_flow_attr *attr,
36 		     u32 *action,
37 		     struct netlink_ext_ack *extack,
38 		     struct mlx5e_tc_act_parse_state *parse_state)
39 {
40 	u8 vlan_idx = attr->total_vlan;
41 
42 	if (vlan_idx >= MLX5_FS_VLAN_DEPTH) {
43 		NL_SET_ERR_MSG_MOD(extack, "Total vlans used is greater than supported");
44 		return -EOPNOTSUPP;
45 	}
46 
47 	if (!mlx5_eswitch_vlan_actions_supported(priv->mdev, vlan_idx)) {
48 		NL_SET_ERR_MSG_MOD(extack, "firmware vlan actions is not supported");
49 		return -EOPNOTSUPP;
50 	}
51 
52 	switch (act->id) {
53 	case FLOW_ACTION_VLAN_POP:
54 		if (vlan_idx)
55 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2;
56 		else
57 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
58 		break;
59 	case FLOW_ACTION_VLAN_PUSH:
60 		attr->vlan_vid[vlan_idx] = act->vlan.vid;
61 		attr->vlan_prio[vlan_idx] = act->vlan.prio;
62 		attr->vlan_proto[vlan_idx] = act->vlan.proto;
63 		if (!attr->vlan_proto[vlan_idx])
64 			attr->vlan_proto[vlan_idx] = htons(ETH_P_8021Q);
65 
66 		if (vlan_idx)
67 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2;
68 		else
69 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
70 		break;
71 	case FLOW_ACTION_VLAN_POP_ETH:
72 		parse_state->eth_pop = true;
73 		break;
74 	case FLOW_ACTION_VLAN_PUSH_ETH:
75 		if (!flow_flag_test(parse_state->flow, L3_TO_L2_DECAP))
76 			return -EOPNOTSUPP;
77 		parse_state->eth_push = true;
78 		memcpy(attr->eth.h_dest, act->vlan_push_eth.dst, ETH_ALEN);
79 		memcpy(attr->eth.h_source, act->vlan_push_eth.src, ETH_ALEN);
80 		break;
81 	default:
82 		NL_SET_ERR_MSG_MOD(extack, "Unexpected action id for VLAN");
83 		return -EINVAL;
84 	}
85 
86 	attr->total_vlan = vlan_idx + 1;
87 
88 	return 0;
89 }
90 
91 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)92 mlx5e_tc_act_vlan_add_push_action(struct mlx5e_priv *priv,
93 				  struct mlx5_flow_attr *attr,
94 				  struct net_device **out_dev,
95 				  struct netlink_ext_ack *extack)
96 {
97 	do {
98 		struct net_device *vlan_dev = *out_dev;
99 		struct flow_action_entry vlan_act = {
100 			.id = FLOW_ACTION_VLAN_PUSH,
101 			.vlan.vid = vlan_dev_vlan_id(vlan_dev),
102 			.vlan.proto = vlan_dev_vlan_proto(vlan_dev),
103 			.vlan.prio = 0,
104 		};
105 		int err;
106 
107 		err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr,
108 					   &attr->action, extack, NULL);
109 		if (err)
110 			return err;
111 
112 		rcu_read_lock();
113 		*out_dev = dev_get_by_index_rcu(dev_net(vlan_dev),
114 						dev_get_iflink(vlan_dev));
115 		rcu_read_unlock();
116 		if (!*out_dev)
117 			return -ENODEV;
118 	} while (is_vlan_dev(*out_dev));
119 
120 	return 0;
121 }
122 
123 int
mlx5e_tc_act_vlan_add_pop_action(struct mlx5e_priv * priv,struct mlx5_flow_attr * attr,struct netlink_ext_ack * extack)124 mlx5e_tc_act_vlan_add_pop_action(struct mlx5e_priv *priv,
125 				 struct mlx5_flow_attr *attr,
126 				 struct netlink_ext_ack *extack)
127 {
128 	struct flow_action_entry vlan_act = {
129 		.id = FLOW_ACTION_VLAN_POP,
130 	};
131 	int nest_level, err = 0;
132 
133 	nest_level = attr->parse_attr->filter_dev->lower_level -
134 						priv->netdev->lower_level;
135 	while (nest_level--) {
136 		err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action,
137 					   extack, NULL);
138 		if (err)
139 			return err;
140 	}
141 
142 	return err;
143 }
144 
145 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)146 tc_act_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state,
147 		  const struct flow_action_entry *act,
148 		  struct mlx5e_priv *priv,
149 		  struct mlx5_flow_attr *attr)
150 {
151 	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
152 	int err;
153 
154 	if (act->id == FLOW_ACTION_VLAN_PUSH &&
155 	    (attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP)) {
156 		/* Replace vlan pop+push with vlan modify */
157 		attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
158 		err = mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB, act,
159 							   attr->parse_attr, &attr->action,
160 							   parse_state->extack);
161 	} else {
162 		err = parse_tc_vlan_action(priv, act, esw_attr, &attr->action,
163 					   parse_state->extack, parse_state);
164 	}
165 
166 	if (err)
167 		return err;
168 
169 	esw_attr->split_count = esw_attr->out_count;
170 	parse_state->if_count = 0;
171 
172 	return 0;
173 }
174 
175 static int
tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state * parse_state,struct mlx5e_priv * priv,struct mlx5_flow_attr * attr)176 tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state,
177 		       struct mlx5e_priv *priv,
178 		       struct mlx5_flow_attr *attr)
179 {
180 	struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
181 	struct netlink_ext_ack *extack = parse_state->extack;
182 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
183 	int err;
184 
185 	if (MLX5_CAP_GEN(esw->dev, prio_tag_required) &&
186 	    attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) {
187 		/* For prio tag mode, replace vlan pop with rewrite vlan prio
188 		 * tag rewrite.
189 		 */
190 		attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
191 		err = add_vlan_prio_tag_rewrite_action(priv, parse_attr,
192 						       &attr->action, extack);
193 		if (err)
194 			return err;
195 	}
196 
197 	return 0;
198 }
199 
200 struct mlx5e_tc_act mlx5e_tc_act_vlan = {
201 	.parse_action = tc_act_parse_vlan,
202 	.post_parse = tc_act_post_parse_vlan,
203 };
204