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 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 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 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 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 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 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 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