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 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 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 47*633ad4b2SRoi Dayan if (!mlx5_eswitch_vlan_actions_supported(priv->mdev, vlan_idx)) { 48*633ad4b2SRoi Dayan NL_SET_ERR_MSG_MOD(extack, "firmware vlan actions is not supported"); 498ee72638SRoi Dayan return -EOPNOTSUPP; 508ee72638SRoi Dayan } 518ee72638SRoi Dayan 52*633ad4b2SRoi Dayan switch (act->id) { 53*633ad4b2SRoi Dayan case FLOW_ACTION_VLAN_POP: 54*633ad4b2SRoi Dayan if (vlan_idx) 558ee72638SRoi Dayan *action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2; 56*633ad4b2SRoi 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 66*633ad4b2SRoi Dayan if (vlan_idx) 678ee72638SRoi Dayan *action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2; 68*633ad4b2SRoi 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 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 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 bool 1458ee72638SRoi Dayan tc_act_can_offload_vlan(struct mlx5e_tc_act_parse_state *parse_state, 1468ee72638SRoi Dayan const struct flow_action_entry *act, 1478be9686dSRoi Dayan int act_index, 1488be9686dSRoi Dayan struct mlx5_flow_attr *attr) 1498ee72638SRoi Dayan { 1508ee72638SRoi Dayan return true; 1518ee72638SRoi Dayan } 1528ee72638SRoi Dayan 1538ee72638SRoi Dayan static int 1548ee72638SRoi Dayan tc_act_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state, 1558ee72638SRoi Dayan const struct flow_action_entry *act, 1568ee72638SRoi Dayan struct mlx5e_priv *priv, 1578ee72638SRoi Dayan struct mlx5_flow_attr *attr) 1588ee72638SRoi Dayan { 1598ee72638SRoi Dayan struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr; 1608ee72638SRoi Dayan int err; 1618ee72638SRoi Dayan 1628ee72638SRoi Dayan if (act->id == FLOW_ACTION_VLAN_PUSH && 1638ee72638SRoi Dayan (attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP)) { 1648ee72638SRoi Dayan /* Replace vlan pop+push with vlan modify */ 1658ee72638SRoi Dayan attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP; 1668ee72638SRoi Dayan err = mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB, act, 16709bf9792SRoi Dayan attr->parse_attr, &attr->action, 16809bf9792SRoi Dayan parse_state->extack); 1698ee72638SRoi Dayan } else { 1708ee72638SRoi Dayan err = parse_tc_vlan_action(priv, act, esw_attr, &attr->action, 171697319b2SMaor Dickman parse_state->extack, parse_state); 1728ee72638SRoi Dayan } 1738ee72638SRoi Dayan 1748ee72638SRoi Dayan if (err) 1758ee72638SRoi Dayan return err; 1768ee72638SRoi Dayan 1778ee72638SRoi Dayan esw_attr->split_count = esw_attr->out_count; 1788ee72638SRoi Dayan 1798ee72638SRoi Dayan return 0; 1808ee72638SRoi Dayan } 1818ee72638SRoi Dayan 182c2208035SRoi Dayan static int 183c2208035SRoi Dayan tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state, 184c2208035SRoi Dayan struct mlx5e_priv *priv, 185c2208035SRoi Dayan struct mlx5_flow_attr *attr) 186c2208035SRoi Dayan { 187c2208035SRoi Dayan struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr; 188c2208035SRoi Dayan struct netlink_ext_ack *extack = parse_state->extack; 189c2208035SRoi Dayan struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; 190c2208035SRoi Dayan int err; 191c2208035SRoi Dayan 192c2208035SRoi Dayan if (MLX5_CAP_GEN(esw->dev, prio_tag_required) && 193c2208035SRoi Dayan attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) { 194c2208035SRoi Dayan /* For prio tag mode, replace vlan pop with rewrite vlan prio 195c2208035SRoi Dayan * tag rewrite. 196c2208035SRoi Dayan */ 197c2208035SRoi Dayan attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP; 19809bf9792SRoi Dayan err = add_vlan_prio_tag_rewrite_action(priv, parse_attr, 199c2208035SRoi Dayan &attr->action, extack); 200c2208035SRoi Dayan if (err) 201c2208035SRoi Dayan return err; 202c2208035SRoi Dayan } 203c2208035SRoi Dayan 204c2208035SRoi Dayan return 0; 205c2208035SRoi Dayan } 206c2208035SRoi Dayan 2078ee72638SRoi Dayan struct mlx5e_tc_act mlx5e_tc_act_vlan = { 2088ee72638SRoi Dayan .can_offload = tc_act_can_offload_vlan, 2098ee72638SRoi Dayan .parse_action = tc_act_parse_vlan, 210c2208035SRoi Dayan .post_parse = tc_act_post_parse_vlan, 2118ee72638SRoi Dayan }; 212