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
10 add_vlan_prio_tag_rewrite_action(struct mlx5e_priv *priv,
11 				 struct mlx5e_tc_flow_parse_attr *parse_attr,
12 				 struct pedit_headers_action *hdrs,
13 				 u32 *action, struct netlink_ext_ack *extack)
14 {
15 	const struct flow_action_entry prio_tag_act = {
16 		.vlan.vid = 0,
17 		.vlan.prio =
18 			MLX5_GET(fte_match_set_lyr_2_4,
19 				 mlx5e_get_match_headers_value(*action,
20 							       &parse_attr->spec),
21 				 first_prio) &
22 			MLX5_GET(fte_match_set_lyr_2_4,
23 				 mlx5e_get_match_headers_criteria(*action,
24 								  &parse_attr->spec),
25 				 first_prio),
26 	};
27 
28 	return mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB,
29 						    &prio_tag_act, parse_attr, hdrs, action,
30 						    extack);
31 }
32 
33 static int
34 parse_tc_vlan_action(struct mlx5e_priv *priv,
35 		     const struct flow_action_entry *act,
36 		     struct mlx5_esw_flow_attr *attr,
37 		     u32 *action,
38 		     struct netlink_ext_ack *extack)
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 	switch (act->id) {
48 	case FLOW_ACTION_VLAN_POP:
49 		if (vlan_idx) {
50 			if (!mlx5_eswitch_vlan_actions_supported(priv->mdev,
51 								 MLX5_FS_VLAN_DEPTH)) {
52 				NL_SET_ERR_MSG_MOD(extack, "vlan pop action is not supported");
53 				return -EOPNOTSUPP;
54 			}
55 
56 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2;
57 		} else {
58 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
59 		}
60 		break;
61 	case FLOW_ACTION_VLAN_PUSH:
62 		attr->vlan_vid[vlan_idx] = act->vlan.vid;
63 		attr->vlan_prio[vlan_idx] = act->vlan.prio;
64 		attr->vlan_proto[vlan_idx] = act->vlan.proto;
65 		if (!attr->vlan_proto[vlan_idx])
66 			attr->vlan_proto[vlan_idx] = htons(ETH_P_8021Q);
67 
68 		if (vlan_idx) {
69 			if (!mlx5_eswitch_vlan_actions_supported(priv->mdev,
70 								 MLX5_FS_VLAN_DEPTH)) {
71 				NL_SET_ERR_MSG_MOD(extack,
72 						   "vlan push action is not supported for vlan depth > 1");
73 				return -EOPNOTSUPP;
74 			}
75 
76 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2;
77 		} else {
78 			if (!mlx5_eswitch_vlan_actions_supported(priv->mdev, 1) &&
79 			    (act->vlan.proto != htons(ETH_P_8021Q) ||
80 			     act->vlan.prio)) {
81 				NL_SET_ERR_MSG_MOD(extack, "vlan push action is not supported");
82 				return -EOPNOTSUPP;
83 			}
84 
85 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
86 		}
87 		break;
88 	default:
89 		NL_SET_ERR_MSG_MOD(extack, "Unexpected action id for VLAN");
90 		return -EINVAL;
91 	}
92 
93 	attr->total_vlan = vlan_idx + 1;
94 
95 	return 0;
96 }
97 
98 int
99 mlx5e_tc_act_vlan_add_push_action(struct mlx5e_priv *priv,
100 				  struct mlx5_flow_attr *attr,
101 				  struct net_device **out_dev,
102 				  struct netlink_ext_ack *extack)
103 {
104 	struct net_device *vlan_dev = *out_dev;
105 	struct flow_action_entry vlan_act = {
106 		.id = FLOW_ACTION_VLAN_PUSH,
107 		.vlan.vid = vlan_dev_vlan_id(vlan_dev),
108 		.vlan.proto = vlan_dev_vlan_proto(vlan_dev),
109 		.vlan.prio = 0,
110 	};
111 	int err;
112 
113 	err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action, extack);
114 	if (err)
115 		return err;
116 
117 	rcu_read_lock();
118 	*out_dev = dev_get_by_index_rcu(dev_net(vlan_dev), dev_get_iflink(vlan_dev));
119 	rcu_read_unlock();
120 	if (!*out_dev)
121 		return -ENODEV;
122 
123 	if (is_vlan_dev(*out_dev))
124 		err = mlx5e_tc_act_vlan_add_push_action(priv, attr, out_dev, extack);
125 
126 	return err;
127 }
128 
129 int
130 mlx5e_tc_act_vlan_add_pop_action(struct mlx5e_priv *priv,
131 				 struct mlx5_flow_attr *attr,
132 				 struct netlink_ext_ack *extack)
133 {
134 	struct flow_action_entry vlan_act = {
135 		.id = FLOW_ACTION_VLAN_POP,
136 	};
137 	int nest_level, err = 0;
138 
139 	nest_level = attr->parse_attr->filter_dev->lower_level -
140 						priv->netdev->lower_level;
141 	while (nest_level--) {
142 		err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action,
143 					   extack);
144 		if (err)
145 			return err;
146 	}
147 
148 	return err;
149 }
150 
151 static bool
152 tc_act_can_offload_vlan(struct mlx5e_tc_act_parse_state *parse_state,
153 			const struct flow_action_entry *act,
154 			int act_index)
155 {
156 	return true;
157 }
158 
159 static int
160 tc_act_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state,
161 		  const struct flow_action_entry *act,
162 		  struct mlx5e_priv *priv,
163 		  struct mlx5_flow_attr *attr)
164 {
165 	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
166 	int err;
167 
168 	if (act->id == FLOW_ACTION_VLAN_PUSH &&
169 	    (attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP)) {
170 		/* Replace vlan pop+push with vlan modify */
171 		attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
172 		err = mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB, act,
173 							   attr->parse_attr, parse_state->hdrs,
174 							   &attr->action, parse_state->extack);
175 	} else {
176 		err = parse_tc_vlan_action(priv, act, esw_attr, &attr->action,
177 					   parse_state->extack);
178 	}
179 
180 	if (err)
181 		return err;
182 
183 	esw_attr->split_count = esw_attr->out_count;
184 
185 	return 0;
186 }
187 
188 static int
189 tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state,
190 		       struct mlx5e_priv *priv,
191 		       struct mlx5_flow_attr *attr)
192 {
193 	struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
194 	struct pedit_headers_action *hdrs = parse_state->hdrs;
195 	struct netlink_ext_ack *extack = parse_state->extack;
196 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
197 	int err;
198 
199 	if (MLX5_CAP_GEN(esw->dev, prio_tag_required) &&
200 	    attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) {
201 		/* For prio tag mode, replace vlan pop with rewrite vlan prio
202 		 * tag rewrite.
203 		 */
204 		attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
205 		err = add_vlan_prio_tag_rewrite_action(priv, parse_attr, hdrs,
206 						       &attr->action, extack);
207 		if (err)
208 			return err;
209 	}
210 
211 	return 0;
212 }
213 
214 struct mlx5e_tc_act mlx5e_tc_act_vlan = {
215 	.can_offload = tc_act_can_offload_vlan,
216 	.parse_action = tc_act_parse_vlan,
217 	.post_parse = tc_act_post_parse_vlan,
218 };
219