1*ab3f3d5eSRoi Dayan // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2*ab3f3d5eSRoi Dayan // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3*ab3f3d5eSRoi Dayan 4*ab3f3d5eSRoi Dayan #include <linux/if_macvlan.h> 5*ab3f3d5eSRoi Dayan #include <linux/if_vlan.h> 6*ab3f3d5eSRoi Dayan #include <net/bareudp.h> 7*ab3f3d5eSRoi Dayan #include <net/bonding.h> 8*ab3f3d5eSRoi Dayan #include "act.h" 9*ab3f3d5eSRoi Dayan #include "vlan.h" 10*ab3f3d5eSRoi Dayan #include "en/tc_tun_encap.h" 11*ab3f3d5eSRoi Dayan #include "en/tc_priv.h" 12*ab3f3d5eSRoi Dayan #include "en_rep.h" 13*ab3f3d5eSRoi Dayan 14*ab3f3d5eSRoi Dayan static bool 15*ab3f3d5eSRoi Dayan same_vf_reps(struct mlx5e_priv *priv, struct net_device *out_dev) 16*ab3f3d5eSRoi Dayan { 17*ab3f3d5eSRoi Dayan return mlx5e_eswitch_vf_rep(priv->netdev) && 18*ab3f3d5eSRoi Dayan priv->netdev == out_dev; 19*ab3f3d5eSRoi Dayan } 20*ab3f3d5eSRoi Dayan 21*ab3f3d5eSRoi Dayan static int 22*ab3f3d5eSRoi Dayan verify_uplink_forwarding(struct mlx5e_priv *priv, 23*ab3f3d5eSRoi Dayan struct mlx5_flow_attr *attr, 24*ab3f3d5eSRoi Dayan struct net_device *out_dev, 25*ab3f3d5eSRoi Dayan struct netlink_ext_ack *extack) 26*ab3f3d5eSRoi Dayan { 27*ab3f3d5eSRoi Dayan struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; 28*ab3f3d5eSRoi Dayan struct mlx5e_rep_priv *rep_priv; 29*ab3f3d5eSRoi Dayan 30*ab3f3d5eSRoi Dayan /* Forwarding non encapsulated traffic between 31*ab3f3d5eSRoi Dayan * uplink ports is allowed only if 32*ab3f3d5eSRoi Dayan * termination_table_raw_traffic cap is set. 33*ab3f3d5eSRoi Dayan * 34*ab3f3d5eSRoi Dayan * Input vport was stored attr->in_rep. 35*ab3f3d5eSRoi Dayan * In LAG case, *priv* is the private data of 36*ab3f3d5eSRoi Dayan * uplink which may be not the input vport. 37*ab3f3d5eSRoi Dayan */ 38*ab3f3d5eSRoi Dayan rep_priv = mlx5e_rep_to_rep_priv(attr->esw_attr->in_rep); 39*ab3f3d5eSRoi Dayan 40*ab3f3d5eSRoi Dayan if (!(mlx5e_eswitch_uplink_rep(rep_priv->netdev) && 41*ab3f3d5eSRoi Dayan mlx5e_eswitch_uplink_rep(out_dev))) 42*ab3f3d5eSRoi Dayan return 0; 43*ab3f3d5eSRoi Dayan 44*ab3f3d5eSRoi Dayan if (!MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, 45*ab3f3d5eSRoi Dayan termination_table_raw_traffic)) { 46*ab3f3d5eSRoi Dayan NL_SET_ERR_MSG_MOD(extack, 47*ab3f3d5eSRoi Dayan "devices are both uplink, can't offload forwarding"); 48*ab3f3d5eSRoi Dayan pr_err("devices %s %s are both uplink, can't offload forwarding\n", 49*ab3f3d5eSRoi Dayan priv->netdev->name, out_dev->name); 50*ab3f3d5eSRoi Dayan return -EOPNOTSUPP; 51*ab3f3d5eSRoi Dayan } else if (out_dev != rep_priv->netdev) { 52*ab3f3d5eSRoi Dayan NL_SET_ERR_MSG_MOD(extack, 53*ab3f3d5eSRoi Dayan "devices are not the same uplink, can't offload forwarding"); 54*ab3f3d5eSRoi Dayan pr_err("devices %s %s are both uplink but not the same, can't offload forwarding\n", 55*ab3f3d5eSRoi Dayan priv->netdev->name, out_dev->name); 56*ab3f3d5eSRoi Dayan return -EOPNOTSUPP; 57*ab3f3d5eSRoi Dayan } 58*ab3f3d5eSRoi Dayan return 0; 59*ab3f3d5eSRoi Dayan } 60*ab3f3d5eSRoi Dayan 61*ab3f3d5eSRoi Dayan static bool 62*ab3f3d5eSRoi Dayan is_duplicated_output_device(struct net_device *dev, 63*ab3f3d5eSRoi Dayan struct net_device *out_dev, 64*ab3f3d5eSRoi Dayan int *ifindexes, int if_count, 65*ab3f3d5eSRoi Dayan struct netlink_ext_ack *extack) 66*ab3f3d5eSRoi Dayan { 67*ab3f3d5eSRoi Dayan int i; 68*ab3f3d5eSRoi Dayan 69*ab3f3d5eSRoi Dayan for (i = 0; i < if_count; i++) { 70*ab3f3d5eSRoi Dayan if (ifindexes[i] == out_dev->ifindex) { 71*ab3f3d5eSRoi Dayan NL_SET_ERR_MSG_MOD(extack, "can't duplicate output to same device"); 72*ab3f3d5eSRoi Dayan netdev_err(dev, "can't duplicate output to same device: %s\n", 73*ab3f3d5eSRoi Dayan out_dev->name); 74*ab3f3d5eSRoi Dayan return true; 75*ab3f3d5eSRoi Dayan } 76*ab3f3d5eSRoi Dayan } 77*ab3f3d5eSRoi Dayan 78*ab3f3d5eSRoi Dayan return false; 79*ab3f3d5eSRoi Dayan } 80*ab3f3d5eSRoi Dayan 81*ab3f3d5eSRoi Dayan static struct net_device * 82*ab3f3d5eSRoi Dayan get_fdb_out_dev(struct net_device *uplink_dev, struct net_device *out_dev) 83*ab3f3d5eSRoi Dayan { 84*ab3f3d5eSRoi Dayan struct net_device *fdb_out_dev = out_dev; 85*ab3f3d5eSRoi Dayan struct net_device *uplink_upper; 86*ab3f3d5eSRoi Dayan 87*ab3f3d5eSRoi Dayan rcu_read_lock(); 88*ab3f3d5eSRoi Dayan uplink_upper = netdev_master_upper_dev_get_rcu(uplink_dev); 89*ab3f3d5eSRoi Dayan if (uplink_upper && netif_is_lag_master(uplink_upper) && 90*ab3f3d5eSRoi Dayan uplink_upper == out_dev) { 91*ab3f3d5eSRoi Dayan fdb_out_dev = uplink_dev; 92*ab3f3d5eSRoi Dayan } else if (netif_is_lag_master(out_dev)) { 93*ab3f3d5eSRoi Dayan fdb_out_dev = bond_option_active_slave_get_rcu(netdev_priv(out_dev)); 94*ab3f3d5eSRoi Dayan if (fdb_out_dev && 95*ab3f3d5eSRoi Dayan (!mlx5e_eswitch_rep(fdb_out_dev) || 96*ab3f3d5eSRoi Dayan !netdev_port_same_parent_id(fdb_out_dev, uplink_dev))) 97*ab3f3d5eSRoi Dayan fdb_out_dev = NULL; 98*ab3f3d5eSRoi Dayan } 99*ab3f3d5eSRoi Dayan rcu_read_unlock(); 100*ab3f3d5eSRoi Dayan return fdb_out_dev; 101*ab3f3d5eSRoi Dayan } 102*ab3f3d5eSRoi Dayan 103*ab3f3d5eSRoi Dayan static bool 104*ab3f3d5eSRoi Dayan tc_act_can_offload_mirred(struct mlx5e_tc_act_parse_state *parse_state, 105*ab3f3d5eSRoi Dayan const struct flow_action_entry *act, 106*ab3f3d5eSRoi Dayan int act_index) 107*ab3f3d5eSRoi Dayan { 108*ab3f3d5eSRoi Dayan struct netlink_ext_ack *extack = parse_state->extack; 109*ab3f3d5eSRoi Dayan struct mlx5e_tc_flow *flow = parse_state->flow; 110*ab3f3d5eSRoi Dayan struct mlx5e_tc_flow_parse_attr *parse_attr; 111*ab3f3d5eSRoi Dayan struct net_device *out_dev = act->dev; 112*ab3f3d5eSRoi Dayan struct mlx5e_priv *priv = flow->priv; 113*ab3f3d5eSRoi Dayan struct mlx5_esw_flow_attr *esw_attr; 114*ab3f3d5eSRoi Dayan 115*ab3f3d5eSRoi Dayan parse_attr = flow->attr->parse_attr; 116*ab3f3d5eSRoi Dayan esw_attr = flow->attr->esw_attr; 117*ab3f3d5eSRoi Dayan 118*ab3f3d5eSRoi Dayan if (!out_dev) { 119*ab3f3d5eSRoi Dayan /* out_dev is NULL when filters with 120*ab3f3d5eSRoi Dayan * non-existing mirred device are replayed to 121*ab3f3d5eSRoi Dayan * the driver. 122*ab3f3d5eSRoi Dayan */ 123*ab3f3d5eSRoi Dayan return false; 124*ab3f3d5eSRoi Dayan } 125*ab3f3d5eSRoi Dayan 126*ab3f3d5eSRoi Dayan if (parse_state->mpls_push && !netif_is_bareudp(out_dev)) { 127*ab3f3d5eSRoi Dayan NL_SET_ERR_MSG_MOD(extack, "mpls is supported only through a bareudp device"); 128*ab3f3d5eSRoi Dayan return false; 129*ab3f3d5eSRoi Dayan } 130*ab3f3d5eSRoi Dayan 131*ab3f3d5eSRoi Dayan if (mlx5e_is_ft_flow(flow) && out_dev == priv->netdev) { 132*ab3f3d5eSRoi Dayan /* Ignore forward to self rules generated 133*ab3f3d5eSRoi Dayan * by adding both mlx5 devs to the flow table 134*ab3f3d5eSRoi Dayan * block on a normal nft offload setup. 135*ab3f3d5eSRoi Dayan */ 136*ab3f3d5eSRoi Dayan return false; 137*ab3f3d5eSRoi Dayan } 138*ab3f3d5eSRoi Dayan 139*ab3f3d5eSRoi Dayan if (esw_attr->out_count >= MLX5_MAX_FLOW_FWD_VPORTS) { 140*ab3f3d5eSRoi Dayan NL_SET_ERR_MSG_MOD(extack, 141*ab3f3d5eSRoi Dayan "can't support more output ports, can't offload forwarding"); 142*ab3f3d5eSRoi Dayan netdev_warn(priv->netdev, 143*ab3f3d5eSRoi Dayan "can't support more than %d output ports, can't offload forwarding\n", 144*ab3f3d5eSRoi Dayan esw_attr->out_count); 145*ab3f3d5eSRoi Dayan return false; 146*ab3f3d5eSRoi Dayan } 147*ab3f3d5eSRoi Dayan 148*ab3f3d5eSRoi Dayan if (parse_state->encap || 149*ab3f3d5eSRoi Dayan netdev_port_same_parent_id(priv->netdev, out_dev) || 150*ab3f3d5eSRoi Dayan netif_is_ovs_master(out_dev)) 151*ab3f3d5eSRoi Dayan return true; 152*ab3f3d5eSRoi Dayan 153*ab3f3d5eSRoi Dayan if (parse_attr->filter_dev != priv->netdev) { 154*ab3f3d5eSRoi Dayan /* All mlx5 devices are called to configure 155*ab3f3d5eSRoi Dayan * high level device filters. Therefore, the 156*ab3f3d5eSRoi Dayan * *attempt* to install a filter on invalid 157*ab3f3d5eSRoi Dayan * eswitch should not trigger an explicit error 158*ab3f3d5eSRoi Dayan */ 159*ab3f3d5eSRoi Dayan return false; 160*ab3f3d5eSRoi Dayan } 161*ab3f3d5eSRoi Dayan 162*ab3f3d5eSRoi Dayan NL_SET_ERR_MSG_MOD(extack, "devices are not on same switch HW, can't offload forwarding"); 163*ab3f3d5eSRoi Dayan netdev_warn(priv->netdev, 164*ab3f3d5eSRoi Dayan "devices %s %s not on same switch HW, can't offload forwarding\n", 165*ab3f3d5eSRoi Dayan netdev_name(priv->netdev), 166*ab3f3d5eSRoi Dayan out_dev->name); 167*ab3f3d5eSRoi Dayan 168*ab3f3d5eSRoi Dayan return false; 169*ab3f3d5eSRoi Dayan } 170*ab3f3d5eSRoi Dayan 171*ab3f3d5eSRoi Dayan static int 172*ab3f3d5eSRoi Dayan parse_mirred_encap(struct mlx5e_tc_act_parse_state *parse_state, 173*ab3f3d5eSRoi Dayan const struct flow_action_entry *act, 174*ab3f3d5eSRoi Dayan struct mlx5_flow_attr *attr) 175*ab3f3d5eSRoi Dayan { 176*ab3f3d5eSRoi Dayan struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr; 177*ab3f3d5eSRoi Dayan struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr; 178*ab3f3d5eSRoi Dayan struct net_device *out_dev = act->dev; 179*ab3f3d5eSRoi Dayan 180*ab3f3d5eSRoi Dayan parse_attr->mirred_ifindex[esw_attr->out_count] = out_dev->ifindex; 181*ab3f3d5eSRoi Dayan parse_attr->tun_info[esw_attr->out_count] = 182*ab3f3d5eSRoi Dayan mlx5e_dup_tun_info(parse_state->tun_info); 183*ab3f3d5eSRoi Dayan 184*ab3f3d5eSRoi Dayan if (!parse_attr->tun_info[esw_attr->out_count]) 185*ab3f3d5eSRoi Dayan return -ENOMEM; 186*ab3f3d5eSRoi Dayan 187*ab3f3d5eSRoi Dayan parse_state->encap = false; 188*ab3f3d5eSRoi Dayan esw_attr->dests[esw_attr->out_count].flags |= MLX5_ESW_DEST_ENCAP; 189*ab3f3d5eSRoi Dayan esw_attr->out_count++; 190*ab3f3d5eSRoi Dayan /* attr->dests[].rep is resolved when we handle encap */ 191*ab3f3d5eSRoi Dayan 192*ab3f3d5eSRoi Dayan return 0; 193*ab3f3d5eSRoi Dayan } 194*ab3f3d5eSRoi Dayan 195*ab3f3d5eSRoi Dayan static int 196*ab3f3d5eSRoi Dayan parse_mirred(struct mlx5e_tc_act_parse_state *parse_state, 197*ab3f3d5eSRoi Dayan const struct flow_action_entry *act, 198*ab3f3d5eSRoi Dayan struct mlx5e_priv *priv, 199*ab3f3d5eSRoi Dayan struct mlx5_flow_attr *attr) 200*ab3f3d5eSRoi Dayan { 201*ab3f3d5eSRoi Dayan struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr; 202*ab3f3d5eSRoi Dayan struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr; 203*ab3f3d5eSRoi Dayan struct netlink_ext_ack *extack = parse_state->extack; 204*ab3f3d5eSRoi Dayan struct mlx5e_rep_priv *rpriv = priv->ppriv; 205*ab3f3d5eSRoi Dayan struct net_device *out_dev = act->dev; 206*ab3f3d5eSRoi Dayan struct net_device *uplink_dev; 207*ab3f3d5eSRoi Dayan struct mlx5e_priv *out_priv; 208*ab3f3d5eSRoi Dayan struct mlx5_eswitch *esw; 209*ab3f3d5eSRoi Dayan int *ifindexes; 210*ab3f3d5eSRoi Dayan int if_count; 211*ab3f3d5eSRoi Dayan int err; 212*ab3f3d5eSRoi Dayan 213*ab3f3d5eSRoi Dayan esw = priv->mdev->priv.eswitch; 214*ab3f3d5eSRoi Dayan uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH); 215*ab3f3d5eSRoi Dayan ifindexes = parse_state->ifindexes; 216*ab3f3d5eSRoi Dayan if_count = parse_state->if_count; 217*ab3f3d5eSRoi Dayan 218*ab3f3d5eSRoi Dayan if (is_duplicated_output_device(priv->netdev, out_dev, ifindexes, if_count, extack)) 219*ab3f3d5eSRoi Dayan return -EOPNOTSUPP; 220*ab3f3d5eSRoi Dayan 221*ab3f3d5eSRoi Dayan parse_state->ifindexes[if_count] = out_dev->ifindex; 222*ab3f3d5eSRoi Dayan parse_state->if_count++; 223*ab3f3d5eSRoi Dayan 224*ab3f3d5eSRoi Dayan out_dev = get_fdb_out_dev(uplink_dev, out_dev); 225*ab3f3d5eSRoi Dayan if (!out_dev) 226*ab3f3d5eSRoi Dayan return -ENODEV; 227*ab3f3d5eSRoi Dayan 228*ab3f3d5eSRoi Dayan if (is_vlan_dev(out_dev)) { 229*ab3f3d5eSRoi Dayan err = mlx5e_tc_act_vlan_add_push_action(priv, attr, &out_dev, extack); 230*ab3f3d5eSRoi Dayan if (err) 231*ab3f3d5eSRoi Dayan return err; 232*ab3f3d5eSRoi Dayan } 233*ab3f3d5eSRoi Dayan 234*ab3f3d5eSRoi Dayan if (is_vlan_dev(parse_attr->filter_dev)) { 235*ab3f3d5eSRoi Dayan err = mlx5e_tc_act_vlan_add_pop_action(priv, attr, extack); 236*ab3f3d5eSRoi Dayan if (err) 237*ab3f3d5eSRoi Dayan return err; 238*ab3f3d5eSRoi Dayan } 239*ab3f3d5eSRoi Dayan 240*ab3f3d5eSRoi Dayan if (netif_is_macvlan(out_dev)) 241*ab3f3d5eSRoi Dayan out_dev = macvlan_dev_real_dev(out_dev); 242*ab3f3d5eSRoi Dayan 243*ab3f3d5eSRoi Dayan err = verify_uplink_forwarding(priv, attr, out_dev, extack); 244*ab3f3d5eSRoi Dayan if (err) 245*ab3f3d5eSRoi Dayan return err; 246*ab3f3d5eSRoi Dayan 247*ab3f3d5eSRoi Dayan if (!mlx5e_is_valid_eswitch_fwd_dev(priv, out_dev)) { 248*ab3f3d5eSRoi Dayan NL_SET_ERR_MSG_MOD(extack, 249*ab3f3d5eSRoi Dayan "devices are not on same switch HW, can't offload forwarding"); 250*ab3f3d5eSRoi Dayan return -EOPNOTSUPP; 251*ab3f3d5eSRoi Dayan } 252*ab3f3d5eSRoi Dayan 253*ab3f3d5eSRoi Dayan if (same_vf_reps(priv, out_dev)) { 254*ab3f3d5eSRoi Dayan NL_SET_ERR_MSG_MOD(extack, "can't forward from a VF to itself"); 255*ab3f3d5eSRoi Dayan return -EOPNOTSUPP; 256*ab3f3d5eSRoi Dayan } 257*ab3f3d5eSRoi Dayan 258*ab3f3d5eSRoi Dayan out_priv = netdev_priv(out_dev); 259*ab3f3d5eSRoi Dayan rpriv = out_priv->ppriv; 260*ab3f3d5eSRoi Dayan esw_attr->dests[esw_attr->out_count].rep = rpriv->rep; 261*ab3f3d5eSRoi Dayan esw_attr->dests[esw_attr->out_count].mdev = out_priv->mdev; 262*ab3f3d5eSRoi Dayan esw_attr->out_count++; 263*ab3f3d5eSRoi Dayan 264*ab3f3d5eSRoi Dayan return 0; 265*ab3f3d5eSRoi Dayan } 266*ab3f3d5eSRoi Dayan 267*ab3f3d5eSRoi Dayan static int 268*ab3f3d5eSRoi Dayan parse_mirred_ovs_master(struct mlx5e_tc_act_parse_state *parse_state, 269*ab3f3d5eSRoi Dayan const struct flow_action_entry *act, 270*ab3f3d5eSRoi Dayan struct mlx5e_priv *priv, 271*ab3f3d5eSRoi Dayan struct mlx5_flow_attr *attr) 272*ab3f3d5eSRoi Dayan { 273*ab3f3d5eSRoi Dayan struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr; 274*ab3f3d5eSRoi Dayan struct net_device *out_dev = act->dev; 275*ab3f3d5eSRoi Dayan int err; 276*ab3f3d5eSRoi Dayan 277*ab3f3d5eSRoi Dayan err = mlx5e_set_fwd_to_int_port_actions(priv, attr, out_dev->ifindex, 278*ab3f3d5eSRoi Dayan MLX5E_TC_INT_PORT_EGRESS, 279*ab3f3d5eSRoi Dayan &attr->action, esw_attr->out_count); 280*ab3f3d5eSRoi Dayan if (err) 281*ab3f3d5eSRoi Dayan return err; 282*ab3f3d5eSRoi Dayan 283*ab3f3d5eSRoi Dayan esw_attr->out_count++; 284*ab3f3d5eSRoi Dayan return 0; 285*ab3f3d5eSRoi Dayan } 286*ab3f3d5eSRoi Dayan 287*ab3f3d5eSRoi Dayan static int 288*ab3f3d5eSRoi Dayan tc_act_parse_mirred(struct mlx5e_tc_act_parse_state *parse_state, 289*ab3f3d5eSRoi Dayan const struct flow_action_entry *act, 290*ab3f3d5eSRoi Dayan struct mlx5e_priv *priv, 291*ab3f3d5eSRoi Dayan struct mlx5_flow_attr *attr) 292*ab3f3d5eSRoi Dayan { 293*ab3f3d5eSRoi Dayan struct net_device *out_dev = act->dev; 294*ab3f3d5eSRoi Dayan int err = -EOPNOTSUPP; 295*ab3f3d5eSRoi Dayan 296*ab3f3d5eSRoi Dayan if (parse_state->encap) 297*ab3f3d5eSRoi Dayan err = parse_mirred_encap(parse_state, act, attr); 298*ab3f3d5eSRoi Dayan else if (netdev_port_same_parent_id(priv->netdev, out_dev)) 299*ab3f3d5eSRoi Dayan err = parse_mirred(parse_state, act, priv, attr); 300*ab3f3d5eSRoi Dayan else if (netif_is_ovs_master(out_dev)) 301*ab3f3d5eSRoi Dayan err = parse_mirred_ovs_master(parse_state, act, priv, attr); 302*ab3f3d5eSRoi Dayan 303*ab3f3d5eSRoi Dayan if (err) 304*ab3f3d5eSRoi Dayan return err; 305*ab3f3d5eSRoi Dayan 306*ab3f3d5eSRoi Dayan attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | 307*ab3f3d5eSRoi Dayan MLX5_FLOW_CONTEXT_ACTION_COUNT; 308*ab3f3d5eSRoi Dayan 309*ab3f3d5eSRoi Dayan return 0; 310*ab3f3d5eSRoi Dayan } 311*ab3f3d5eSRoi Dayan 312*ab3f3d5eSRoi Dayan struct mlx5e_tc_act mlx5e_tc_act_mirred = { 313*ab3f3d5eSRoi Dayan .can_offload = tc_act_can_offload_mirred, 314*ab3f3d5eSRoi Dayan .parse_action = tc_act_parse_mirred, 315*ab3f3d5eSRoi Dayan }; 316