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 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 case FLOW_ACTION_VLAN_POP_ETH:
89 parse_state->eth_pop = true;
90 break;
91 case FLOW_ACTION_VLAN_PUSH_ETH:
92 if (!flow_flag_test(parse_state->flow, L3_TO_L2_DECAP))
93 return -EOPNOTSUPP;
94 parse_state->eth_push = true;
95 memcpy(attr->eth.h_dest, act->vlan_push_eth.dst, ETH_ALEN);
96 memcpy(attr->eth.h_source, act->vlan_push_eth.src, ETH_ALEN);
97 break;
98 default:
99 NL_SET_ERR_MSG_MOD(extack, "Unexpected action id for VLAN");
100 return -EINVAL;
101 }
102
103 attr->total_vlan = vlan_idx + 1;
104
105 return 0;
106 }
107
108 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)109 mlx5e_tc_act_vlan_add_push_action(struct mlx5e_priv *priv,
110 struct mlx5_flow_attr *attr,
111 struct net_device **out_dev,
112 struct netlink_ext_ack *extack)
113 {
114 struct net_device *vlan_dev = *out_dev;
115 struct flow_action_entry vlan_act = {
116 .id = FLOW_ACTION_VLAN_PUSH,
117 .vlan.vid = vlan_dev_vlan_id(vlan_dev),
118 .vlan.proto = vlan_dev_vlan_proto(vlan_dev),
119 .vlan.prio = 0,
120 };
121 int err;
122
123 err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action, extack, NULL);
124 if (err)
125 return err;
126
127 rcu_read_lock();
128 *out_dev = dev_get_by_index_rcu(dev_net(vlan_dev), dev_get_iflink(vlan_dev));
129 rcu_read_unlock();
130 if (!*out_dev)
131 return -ENODEV;
132
133 if (is_vlan_dev(*out_dev))
134 err = mlx5e_tc_act_vlan_add_push_action(priv, attr, out_dev, extack);
135
136 return err;
137 }
138
139 int
mlx5e_tc_act_vlan_add_pop_action(struct mlx5e_priv * priv,struct mlx5_flow_attr * attr,struct netlink_ext_ack * extack)140 mlx5e_tc_act_vlan_add_pop_action(struct mlx5e_priv *priv,
141 struct mlx5_flow_attr *attr,
142 struct netlink_ext_ack *extack)
143 {
144 struct flow_action_entry vlan_act = {
145 .id = FLOW_ACTION_VLAN_POP,
146 };
147 int nest_level, err = 0;
148
149 nest_level = attr->parse_attr->filter_dev->lower_level -
150 priv->netdev->lower_level;
151 while (nest_level--) {
152 err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action,
153 extack, NULL);
154 if (err)
155 return err;
156 }
157
158 return err;
159 }
160
161 static bool
tc_act_can_offload_vlan(struct mlx5e_tc_act_parse_state * parse_state,const struct flow_action_entry * act,int act_index,struct mlx5_flow_attr * attr)162 tc_act_can_offload_vlan(struct mlx5e_tc_act_parse_state *parse_state,
163 const struct flow_action_entry *act,
164 int act_index,
165 struct mlx5_flow_attr *attr)
166 {
167 return true;
168 }
169
170 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)171 tc_act_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state,
172 const struct flow_action_entry *act,
173 struct mlx5e_priv *priv,
174 struct mlx5_flow_attr *attr)
175 {
176 struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
177 int err;
178
179 if (act->id == FLOW_ACTION_VLAN_PUSH &&
180 (attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP)) {
181 /* Replace vlan pop+push with vlan modify */
182 attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
183 err = mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB, act,
184 attr->parse_attr, &attr->action,
185 parse_state->extack);
186 } else {
187 err = parse_tc_vlan_action(priv, act, esw_attr, &attr->action,
188 parse_state->extack, parse_state);
189 }
190
191 if (err)
192 return err;
193
194 esw_attr->split_count = esw_attr->out_count;
195
196 return 0;
197 }
198
199 static int
tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state * parse_state,struct mlx5e_priv * priv,struct mlx5_flow_attr * attr)200 tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state,
201 struct mlx5e_priv *priv,
202 struct mlx5_flow_attr *attr)
203 {
204 struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
205 struct netlink_ext_ack *extack = parse_state->extack;
206 struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
207 int err;
208
209 if (MLX5_CAP_GEN(esw->dev, prio_tag_required) &&
210 attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) {
211 /* For prio tag mode, replace vlan pop with rewrite vlan prio
212 * tag rewrite.
213 */
214 attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
215 err = add_vlan_prio_tag_rewrite_action(priv, parse_attr,
216 &attr->action, extack);
217 if (err)
218 return err;
219 }
220
221 return 0;
222 }
223
224 struct mlx5e_tc_act mlx5e_tc_act_vlan = {
225 .can_offload = tc_act_can_offload_vlan,
226 .parse_action = tc_act_parse_vlan,
227 .post_parse = tc_act_post_parse_vlan,
228 };
229