Merge branch 'bnxt_en-Updates-for-net-next'
authorDavid S. Miller <davem@davemloft.net>
Thu, 31 Oct 2019 21:48:31 +0000 (14:48 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 31 Oct 2019 21:48:31 +0000 (14:48 -0700)
Michael Chan says:

====================
bnxt_en: Updates for net-next.

This patch series adds TC Flower tunnel decap and rewrite actions in
the first 4 patches.  The next 3 patches integrates the recently
added error recovery with the RDMA driver by calling the proper
hooks to stop and start.

v2: Fix pointer alignment issue in patch #1.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/broadcom/bnxt/bnxt.h
drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c
drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h
drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h

index c24caaa..477b8d7 100644 (file)
@@ -8762,6 +8762,8 @@ static int bnxt_hwrm_if_change(struct bnxt *bp, bool up)
        }
        if (resc_reinit || fw_reset) {
                if (fw_reset) {
+                       if (!test_bit(BNXT_STATE_IN_FW_RESET, &bp->state))
+                               bnxt_ulp_stop(bp);
                        rc = bnxt_fw_init_one(bp);
                        if (rc) {
                                set_bit(BNXT_STATE_ABORT_ERR, &bp->state);
@@ -9224,13 +9226,16 @@ static int bnxt_open(struct net_device *dev)
        if (rc) {
                bnxt_hwrm_if_change(bp, false);
        } else {
-               if (test_and_clear_bit(BNXT_STATE_FW_RESET_DET, &bp->state) &&
-                   BNXT_PF(bp)) {
-                       struct bnxt_pf_info *pf = &bp->pf;
-                       int n = pf->active_vfs;
+               if (test_and_clear_bit(BNXT_STATE_FW_RESET_DET, &bp->state)) {
+                       if (BNXT_PF(bp)) {
+                               struct bnxt_pf_info *pf = &bp->pf;
+                               int n = pf->active_vfs;
 
-                       if (n)
-                               bnxt_cfg_hw_sriov(bp, &n, true);
+                               if (n)
+                                       bnxt_cfg_hw_sriov(bp, &n, true);
+                       }
+                       if (!test_bit(BNXT_STATE_IN_FW_RESET, &bp->state))
+                               bnxt_ulp_start(bp, 0);
                }
                bnxt_hwmon_open(bp);
        }
@@ -9927,12 +9932,15 @@ static void bnxt_reset_task(struct bnxt *bp, bool silent)
        if (netif_running(bp->dev)) {
                int rc;
 
-               if (!silent)
+               if (silent) {
+                       bnxt_close_nic(bp, false, false);
+                       bnxt_open_nic(bp, false, false);
+               } else {
                        bnxt_ulp_stop(bp);
-               bnxt_close_nic(bp, false, false);
-               rc = bnxt_open_nic(bp, false, false);
-               if (!silent && !rc)
-                       bnxt_ulp_start(bp);
+                       bnxt_close_nic(bp, true, false);
+                       rc = bnxt_open_nic(bp, true, false);
+                       bnxt_ulp_start(bp, rc);
+               }
        }
 }
 
@@ -10048,8 +10056,8 @@ static void bnxt_reset(struct bnxt *bp, bool silent)
 
 static void bnxt_fw_reset_close(struct bnxt *bp)
 {
+       bnxt_ulp_stop(bp);
        __bnxt_close_nic(bp, true, false);
-       bnxt_ulp_irq_stop(bp);
        bnxt_clear_int_mode(bp);
        bnxt_hwrm_func_drv_unrgtr(bp);
        bnxt_free_ctx_mem(bp);
@@ -10731,13 +10739,13 @@ static void bnxt_fw_reset_task(struct work_struct *work)
                        clear_bit(BNXT_STATE_IN_FW_RESET, &bp->state);
                        dev_close(bp->dev);
                }
-               bnxt_ulp_irq_restart(bp, rc);
-               rtnl_unlock();
 
                bp->fw_reset_state = 0;
                /* Make sure fw_reset_state is 0 before clearing the flag */
                smp_mb__before_atomic();
                clear_bit(BNXT_STATE_IN_FW_RESET, &bp->state);
+               bnxt_ulp_start(bp, rc);
+               rtnl_unlock();
                break;
        }
        return;
@@ -10945,7 +10953,7 @@ static int bnxt_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
        }
 }
 
-static LIST_HEAD(bnxt_block_cb_list);
+LIST_HEAD(bnxt_block_cb_list);
 
 static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type,
                         void *type_data)
@@ -11893,6 +11901,7 @@ static int bnxt_suspend(struct device *device)
        int rc = 0;
 
        rtnl_lock();
+       bnxt_ulp_stop(bp);
        if (netif_running(dev)) {
                netif_device_detach(dev);
                rc = bnxt_close(dev);
@@ -11926,6 +11935,7 @@ static int bnxt_resume(struct device *device)
        }
 
 resume_exit:
+       bnxt_ulp_start(bp, rc);
        rtnl_unlock();
        return rc;
 }
@@ -12005,10 +12015,9 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)
                if (!err && netif_running(netdev))
                        err = bnxt_open(netdev);
 
-               if (!err) {
+               if (!err)
                        result = PCI_ERS_RESULT_RECOVERED;
-                       bnxt_ulp_start(bp);
-               }
+               bnxt_ulp_start(bp, err);
        }
 
        if (result != PCI_ERS_RESULT_RECOVERED && netif_running(netdev))
index 3e7d1fb..a3545c8 100644 (file)
@@ -29,6 +29,8 @@
 #include <linux/firmware/broadcom/tee_bnxt_fw.h>
 #endif
 
+extern struct list_head bnxt_block_cb_list;
+
 struct page_pool;
 
 struct tx_bd {
@@ -1244,6 +1246,14 @@ struct bnxt_tc_flow_stats {
        u64             bytes;
 };
 
+#ifdef CONFIG_BNXT_FLOWER_OFFLOAD
+struct bnxt_flower_indr_block_cb_priv {
+       struct net_device *tunnel_netdev;
+       struct bnxt *bp;
+       struct list_head list;
+};
+#endif
+
 struct bnxt_tc_info {
        bool                            enabled;
 
@@ -1821,6 +1831,8 @@ struct bnxt {
        u16                     *cfa_code_map; /* cfa_code -> vf_idx map */
        u8                      switch_id[8];
        struct bnxt_tc_info     *tc_info;
+       struct list_head        tc_indr_block_list;
+       struct notifier_block   tc_netdev_nb;
        struct dentry           *debugfs_pdev;
        struct device           *hwmon_dev;
 };
index c8062d0..174412a 100644 (file)
@@ -16,7 +16,9 @@
 #include <net/tc_act/tc_skbedit.h>
 #include <net/tc_act/tc_mirred.h>
 #include <net/tc_act/tc_vlan.h>
+#include <net/tc_act/tc_pedit.h>
 #include <net/tc_act/tc_tunnel_key.h>
+#include <net/vxlan.h>
 
 #include "bnxt_hsi.h"
 #include "bnxt.h"
@@ -36,6 +38,8 @@
 #define is_vid_exactmatch(vlan_tci_mask)       \
        ((ntohs(vlan_tci_mask) & VLAN_VID_MASK) == VLAN_VID_MASK)
 
+static bool is_wildcard(void *mask, int len);
+static bool is_exactmatch(void *mask, int len);
 /* Return the dst fid of the func for flow forwarding
  * For PFs: src_fid is the fid of the PF
  * For VF-reps: src_fid the fid of the VF
@@ -111,10 +115,182 @@ static int bnxt_tc_parse_tunnel_set(struct bnxt *bp,
        return 0;
 }
 
+/* Key & Mask from the stack comes unaligned in multiple iterations of 4 bytes
+ * each(u32).
+ * This routine consolidates such multiple unaligned values into one
+ * field each for Key & Mask (for src and dst macs separately)
+ * For example,
+ *                     Mask/Key        Offset  Iteration
+ *                     ==========      ======  =========
+ *     dst mac         0xffffffff      0       1
+ *     dst mac         0x0000ffff      4       2
+ *
+ *     src mac         0xffff0000      4       1
+ *     src mac         0xffffffff      8       2
+ *
+ * The above combination coming from the stack will be consolidated as
+ *                     Mask/Key
+ *                     ==============
+ *     src mac:        0xffffffffffff
+ *     dst mac:        0xffffffffffff
+ */
+static void bnxt_set_l2_key_mask(u32 part_key, u32 part_mask,
+                                u8 *actual_key, u8 *actual_mask)
+{
+       u32 key = get_unaligned((u32 *)actual_key);
+       u32 mask = get_unaligned((u32 *)actual_mask);
+
+       part_key &= part_mask;
+       part_key |= key & ~part_mask;
+
+       put_unaligned(mask | part_mask, (u32 *)actual_mask);
+       put_unaligned(part_key, (u32 *)actual_key);
+}
+
+static int
+bnxt_fill_l2_rewrite_fields(struct bnxt_tc_actions *actions,
+                           u16 *eth_addr, u16 *eth_addr_mask)
+{
+       u16 *p;
+       int j;
+
+       if (unlikely(bnxt_eth_addr_key_mask_invalid(eth_addr, eth_addr_mask)))
+               return -EINVAL;
+
+       if (!is_wildcard(&eth_addr_mask[0], ETH_ALEN)) {
+               if (!is_exactmatch(&eth_addr_mask[0], ETH_ALEN))
+                       return -EINVAL;
+               /* FW expects dmac to be in u16 array format */
+               p = eth_addr;
+               for (j = 0; j < 3; j++)
+                       actions->l2_rewrite_dmac[j] = cpu_to_be16(*(p + j));
+       }
+
+       if (!is_wildcard(&eth_addr_mask[ETH_ALEN], ETH_ALEN)) {
+               if (!is_exactmatch(&eth_addr_mask[ETH_ALEN], ETH_ALEN))
+                       return -EINVAL;
+               /* FW expects smac to be in u16 array format */
+               p = &eth_addr[ETH_ALEN / 2];
+               for (j = 0; j < 3; j++)
+                       actions->l2_rewrite_smac[j] = cpu_to_be16(*(p + j));
+       }
+
+       return 0;
+}
+
+static int
+bnxt_tc_parse_pedit(struct bnxt *bp, struct bnxt_tc_actions *actions,
+                   struct flow_action_entry *act, int act_idx, u8 *eth_addr,
+                   u8 *eth_addr_mask)
+{
+       size_t offset_of_ip6_daddr = offsetof(struct ipv6hdr, daddr);
+       size_t offset_of_ip6_saddr = offsetof(struct ipv6hdr, saddr);
+       u32 mask, val, offset, idx;
+       u8 htype;
+
+       offset = act->mangle.offset;
+       htype = act->mangle.htype;
+       mask = ~act->mangle.mask;
+       val = act->mangle.val;
+
+       switch (htype) {
+       case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
+               if (offset > PEDIT_OFFSET_SMAC_LAST_4_BYTES) {
+                       netdev_err(bp->dev,
+                                  "%s: eth_hdr: Invalid pedit field\n",
+                                  __func__);
+                       return -EINVAL;
+               }
+               actions->flags |= BNXT_TC_ACTION_FLAG_L2_REWRITE;
+
+               bnxt_set_l2_key_mask(val, mask, &eth_addr[offset],
+                                    &eth_addr_mask[offset]);
+               break;
+       case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
+               actions->flags |= BNXT_TC_ACTION_FLAG_NAT_XLATE;
+               actions->nat.l3_is_ipv4 = true;
+               if (offset ==  offsetof(struct iphdr, saddr)) {
+                       actions->nat.src_xlate = true;
+                       actions->nat.l3.ipv4.saddr.s_addr = htonl(val);
+               } else if (offset ==  offsetof(struct iphdr, daddr)) {
+                       actions->nat.src_xlate = false;
+                       actions->nat.l3.ipv4.daddr.s_addr = htonl(val);
+               } else {
+                       netdev_err(bp->dev,
+                                  "%s: IPv4_hdr: Invalid pedit field\n",
+                                  __func__);
+                       return -EINVAL;
+               }
+
+               netdev_dbg(bp->dev, "nat.src_xlate = %d src IP: %pI4 dst ip : %pI4\n",
+                          actions->nat.src_xlate, &actions->nat.l3.ipv4.saddr,
+                          &actions->nat.l3.ipv4.daddr);
+               break;
+
+       case FLOW_ACT_MANGLE_HDR_TYPE_IP6:
+               actions->flags |= BNXT_TC_ACTION_FLAG_NAT_XLATE;
+               actions->nat.l3_is_ipv4 = false;
+               if (offset >= offsetof(struct ipv6hdr, saddr) &&
+                   offset < offset_of_ip6_daddr) {
+                       /* 16 byte IPv6 address comes in 4 iterations of
+                        * 4byte chunks each
+                        */
+                       actions->nat.src_xlate = true;
+                       idx = (offset - offset_of_ip6_saddr) / 4;
+                       /* First 4bytes will be copied to idx 0 and so on */
+                       actions->nat.l3.ipv6.saddr.s6_addr32[idx] = htonl(val);
+               } else if (offset >= offset_of_ip6_daddr &&
+                          offset < offset_of_ip6_daddr + 16) {
+                       actions->nat.src_xlate = false;
+                       idx = (offset - offset_of_ip6_daddr) / 4;
+                       actions->nat.l3.ipv6.saddr.s6_addr32[idx] = htonl(val);
+               } else {
+                       netdev_err(bp->dev,
+                                  "%s: IPv6_hdr: Invalid pedit field\n",
+                                  __func__);
+                       return -EINVAL;
+               }
+               break;
+       case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
+       case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
+               /* HW does not support L4 rewrite alone without L3
+                * rewrite
+                */
+               if (!(actions->flags & BNXT_TC_ACTION_FLAG_NAT_XLATE)) {
+                       netdev_err(bp->dev,
+                                  "Need to specify L3 rewrite as well\n");
+                       return -EINVAL;
+               }
+               if (actions->nat.src_xlate)
+                       actions->nat.l4.ports.sport = htons(val);
+               else
+                       actions->nat.l4.ports.dport = htons(val);
+               netdev_dbg(bp->dev, "actions->nat.sport = %d dport = %d\n",
+                          actions->nat.l4.ports.sport,
+                          actions->nat.l4.ports.dport);
+               break;
+       default:
+               netdev_err(bp->dev, "%s: Unsupported pedit hdr type\n",
+                          __func__);
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static int bnxt_tc_parse_actions(struct bnxt *bp,
                                 struct bnxt_tc_actions *actions,
                                 struct flow_action *flow_action)
 {
+       /* Used to store the L2 rewrite mask for dmac (6 bytes) followed by
+        * smac (6 bytes) if rewrite of both is specified, otherwise either
+        * dmac or smac
+        */
+       u16 eth_addr_mask[ETH_ALEN] = { 0 };
+       /* Used to store the L2 rewrite key for dmac (6 bytes) followed by
+        * smac (6 bytes) if rewrite of both is specified, otherwise either
+        * dmac or smac
+        */
+       u16 eth_addr[ETH_ALEN] = { 0 };
        struct flow_action_entry *act;
        int i, rc;
 
@@ -148,11 +324,26 @@ static int bnxt_tc_parse_actions(struct bnxt *bp,
                case FLOW_ACTION_TUNNEL_DECAP:
                        actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_DECAP;
                        break;
+               /* Packet edit: L2 rewrite, NAT, NAPT */
+               case FLOW_ACTION_MANGLE:
+                       rc = bnxt_tc_parse_pedit(bp, actions, act, i,
+                                                (u8 *)eth_addr,
+                                                (u8 *)eth_addr_mask);
+                       if (rc)
+                               return rc;
+                       break;
                default:
                        break;
                }
        }
 
+       if (actions->flags & BNXT_TC_ACTION_FLAG_L2_REWRITE) {
+               rc = bnxt_fill_l2_rewrite_fields(actions, eth_addr,
+                                                eth_addr_mask);
+               if (rc)
+                       return rc;
+       }
+
        if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) {
                if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) {
                        /* dst_fid is PF's fid */
@@ -401,6 +592,76 @@ static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow,
        req.src_fid = cpu_to_le16(flow->src_fid);
        req.ref_flow_handle = ref_flow_handle;
 
+       if (actions->flags & BNXT_TC_ACTION_FLAG_L2_REWRITE) {
+               memcpy(req.l2_rewrite_dmac, actions->l2_rewrite_dmac,
+                      ETH_ALEN);
+               memcpy(req.l2_rewrite_smac, actions->l2_rewrite_smac,
+                      ETH_ALEN);
+               action_flags |=
+                       CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE;
+       }
+
+       if (actions->flags & BNXT_TC_ACTION_FLAG_NAT_XLATE) {
+               if (actions->nat.l3_is_ipv4) {
+                       action_flags |=
+                               CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_IPV4_ADDRESS;
+
+                       if (actions->nat.src_xlate) {
+                               action_flags |=
+                                       CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC;
+                               /* L3 source rewrite */
+                               req.nat_ip_address[0] =
+                                       actions->nat.l3.ipv4.saddr.s_addr;
+                               /* L4 source port */
+                               if (actions->nat.l4.ports.sport)
+                                       req.nat_port =
+                                               actions->nat.l4.ports.sport;
+                       } else {
+                               action_flags |=
+                                       CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST;
+                               /* L3 destination rewrite */
+                               req.nat_ip_address[0] =
+                                       actions->nat.l3.ipv4.daddr.s_addr;
+                               /* L4 destination port */
+                               if (actions->nat.l4.ports.dport)
+                                       req.nat_port =
+                                               actions->nat.l4.ports.dport;
+                       }
+                       netdev_dbg(bp->dev,
+                                  "req.nat_ip_address: %pI4 src_xlate: %d req.nat_port: %x\n",
+                                  req.nat_ip_address, actions->nat.src_xlate,
+                                  req.nat_port);
+               } else {
+                       if (actions->nat.src_xlate) {
+                               action_flags |=
+                                       CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC;
+                               /* L3 source rewrite */
+                               memcpy(req.nat_ip_address,
+                                      actions->nat.l3.ipv6.saddr.s6_addr32,
+                                      sizeof(req.nat_ip_address));
+                               /* L4 source port */
+                               if (actions->nat.l4.ports.sport)
+                                       req.nat_port =
+                                               actions->nat.l4.ports.sport;
+                       } else {
+                               action_flags |=
+                                       CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST;
+                               /* L3 destination rewrite */
+                               memcpy(req.nat_ip_address,
+                                      actions->nat.l3.ipv6.daddr.s6_addr32,
+                                      sizeof(req.nat_ip_address));
+                               /* L4 destination port */
+                               if (actions->nat.l4.ports.dport)
+                                       req.nat_port =
+                                               actions->nat.l4.ports.dport;
+                       }
+                       netdev_dbg(bp->dev,
+                                  "req.nat_ip_address: %pI6 src_xlate: %d req.nat_port: %x\n",
+                                  req.nat_ip_address, actions->nat.src_xlate,
+                                  req.nat_port);
+               }
+       }
+
        if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP ||
            actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) {
                req.tunnel_handle = tunnel_handle;
@@ -1274,7 +1535,8 @@ static int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid,
 
        if (!bnxt_tc_can_offload(bp, flow)) {
                rc = -EOPNOTSUPP;
-               goto free_node;
+               kfree_rcu(new_node, rcu);
+               return rc;
        }
 
        /* If a flow exists with the same cookie, delete it */
@@ -1580,6 +1842,147 @@ int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid,
        }
 }
 
+static int bnxt_tc_setup_indr_block_cb(enum tc_setup_type type,
+                                      void *type_data, void *cb_priv)
+{
+       struct bnxt_flower_indr_block_cb_priv *priv = cb_priv;
+       struct flow_cls_offload *flower = type_data;
+       struct bnxt *bp = priv->bp;
+
+       if (flower->common.chain_index)
+               return -EOPNOTSUPP;
+
+       switch (type) {
+       case TC_SETUP_CLSFLOWER:
+               return bnxt_tc_setup_flower(bp, bp->pf.fw_fid, flower);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static struct bnxt_flower_indr_block_cb_priv *
+bnxt_tc_indr_block_cb_lookup(struct bnxt *bp, struct net_device *netdev)
+{
+       struct bnxt_flower_indr_block_cb_priv *cb_priv;
+
+       /* All callback list access should be protected by RTNL. */
+       ASSERT_RTNL();
+
+       list_for_each_entry(cb_priv, &bp->tc_indr_block_list, list)
+               if (cb_priv->tunnel_netdev == netdev)
+                       return cb_priv;
+
+       return NULL;
+}
+
+static void bnxt_tc_setup_indr_rel(void *cb_priv)
+{
+       struct bnxt_flower_indr_block_cb_priv *priv = cb_priv;
+
+       list_del(&priv->list);
+       kfree(priv);
+}
+
+static int bnxt_tc_setup_indr_block(struct net_device *netdev, struct bnxt *bp,
+                                   struct flow_block_offload *f)
+{
+       struct bnxt_flower_indr_block_cb_priv *cb_priv;
+       struct flow_block_cb *block_cb;
+
+       if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+               return -EOPNOTSUPP;
+
+       switch (f->command) {
+       case FLOW_BLOCK_BIND:
+               cb_priv = kmalloc(sizeof(*cb_priv), GFP_KERNEL);
+               if (!cb_priv)
+                       return -ENOMEM;
+
+               cb_priv->tunnel_netdev = netdev;
+               cb_priv->bp = bp;
+               list_add(&cb_priv->list, &bp->tc_indr_block_list);
+
+               block_cb = flow_block_cb_alloc(bnxt_tc_setup_indr_block_cb,
+                                              cb_priv, cb_priv,
+                                              bnxt_tc_setup_indr_rel);
+               if (IS_ERR(block_cb)) {
+                       list_del(&cb_priv->list);
+                       kfree(cb_priv);
+                       return PTR_ERR(block_cb);
+               }
+
+               flow_block_cb_add(block_cb, f);
+               list_add_tail(&block_cb->driver_list, &bnxt_block_cb_list);
+               break;
+       case FLOW_BLOCK_UNBIND:
+               cb_priv = bnxt_tc_indr_block_cb_lookup(bp, netdev);
+               if (!cb_priv)
+                       return -ENOENT;
+
+               block_cb = flow_block_cb_lookup(f->block,
+                                               bnxt_tc_setup_indr_block_cb,
+                                               cb_priv);
+               if (!block_cb)
+                       return -ENOENT;
+
+               flow_block_cb_remove(block_cb, f);
+               list_del(&block_cb->driver_list);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int bnxt_tc_setup_indr_cb(struct net_device *netdev, void *cb_priv,
+                                enum tc_setup_type type, void *type_data)
+{
+       switch (type) {
+       case TC_SETUP_BLOCK:
+               return bnxt_tc_setup_indr_block(netdev, cb_priv, type_data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static bool bnxt_is_netdev_indr_offload(struct net_device *netdev)
+{
+       return netif_is_vxlan(netdev);
+}
+
+static int bnxt_tc_indr_block_event(struct notifier_block *nb,
+                                   unsigned long event, void *ptr)
+{
+       struct net_device *netdev;
+       struct bnxt *bp;
+       int rc;
+
+       netdev = netdev_notifier_info_to_dev(ptr);
+       if (!bnxt_is_netdev_indr_offload(netdev))
+               return NOTIFY_OK;
+
+       bp = container_of(nb, struct bnxt, tc_netdev_nb);
+
+       switch (event) {
+       case NETDEV_REGISTER:
+               rc = __flow_indr_block_cb_register(netdev, bp,
+                                                  bnxt_tc_setup_indr_cb,
+                                                  bp);
+               if (rc)
+                       netdev_info(bp->dev,
+                                   "Failed to register indirect blk: dev: %s",
+                                   netdev->name);
+               break;
+       case NETDEV_UNREGISTER:
+               __flow_indr_block_cb_unregister(netdev,
+                                               bnxt_tc_setup_indr_cb,
+                                               bp);
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
 static const struct rhashtable_params bnxt_tc_flow_ht_params = {
        .head_offset = offsetof(struct bnxt_tc_flow_node, node),
        .key_offset = offsetof(struct bnxt_tc_flow_node, cookie),
@@ -1663,7 +2066,15 @@ int bnxt_init_tc(struct bnxt *bp)
        bp->dev->hw_features |= NETIF_F_HW_TC;
        bp->dev->features |= NETIF_F_HW_TC;
        bp->tc_info = tc_info;
-       return 0;
+
+       /* init indirect block notifications */
+       INIT_LIST_HEAD(&bp->tc_indr_block_list);
+       bp->tc_netdev_nb.notifier_call = bnxt_tc_indr_block_event;
+       rc = register_netdevice_notifier(&bp->tc_netdev_nb);
+       if (!rc)
+               return 0;
+
+       rhashtable_destroy(&tc_info->encap_table);
 
 destroy_decap_table:
        rhashtable_destroy(&tc_info->decap_table);
@@ -1685,6 +2096,7 @@ void bnxt_shutdown_tc(struct bnxt *bp)
        if (!bnxt_tc_flower_enabled(bp))
                return;
 
+       unregister_netdevice_notifier(&bp->tc_netdev_nb);
        rhashtable_destroy(&tc_info->flow_table);
        rhashtable_destroy(&tc_info->l2_table);
        rhashtable_destroy(&tc_info->decap_l2_table);
index 4f05305..2867549 100644 (file)
@@ -62,6 +62,12 @@ struct bnxt_tc_tunnel_key {
        __be32                  id;
 };
 
+#define bnxt_eth_addr_key_mask_invalid(eth_addr, eth_addr_mask)                \
+       ((is_wildcard(&(eth_addr)[0], ETH_ALEN) &&                      \
+        is_wildcard(&(eth_addr)[ETH_ALEN], ETH_ALEN)) ||               \
+       (is_wildcard(&(eth_addr_mask)[0], ETH_ALEN) &&                  \
+        is_wildcard(&(eth_addr_mask)[ETH_ALEN], ETH_ALEN)))
+
 struct bnxt_tc_actions {
        u32                             flags;
 #define BNXT_TC_ACTION_FLAG_FWD                        BIT(0)
@@ -71,6 +77,8 @@ struct bnxt_tc_actions {
 #define BNXT_TC_ACTION_FLAG_DROP               BIT(5)
 #define BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP       BIT(6)
 #define BNXT_TC_ACTION_FLAG_TUNNEL_DECAP       BIT(7)
+#define BNXT_TC_ACTION_FLAG_L2_REWRITE         BIT(8)
+#define BNXT_TC_ACTION_FLAG_NAT_XLATE          BIT(9)
 
        u16                             dst_fid;
        struct net_device               *dst_dev;
@@ -79,6 +87,18 @@ struct bnxt_tc_actions {
 
        /* tunnel encap */
        struct ip_tunnel_key            tun_encap_key;
+#define        PEDIT_OFFSET_SMAC_LAST_4_BYTES          0x8
+       __be16                          l2_rewrite_dmac[3];
+       __be16                          l2_rewrite_smac[3];
+       struct {
+               bool src_xlate;  /* true => translate src,
+                                 * false => translate dst
+                                 * Mutually exclusive, i.e cannot set both
+                                 */
+               bool l3_is_ipv4; /* false means L3 is ipv6 */
+               struct bnxt_tc_l3_key l3;
+               struct bnxt_tc_l4_key l4;
+       } nat;
 };
 
 struct bnxt_tc_flow {
index b2c1609..077fd10 100644 (file)
@@ -182,7 +182,7 @@ static int bnxt_free_msix_vecs(struct bnxt_en_dev *edev, int ulp_id)
 
        edev->ulp_tbl[ulp_id].msix_requested = 0;
        edev->flags &= ~BNXT_EN_FLAG_MSIX_REQUESTED;
-       if (netif_running(dev)) {
+       if (netif_running(dev) && !(edev->flags & BNXT_EN_FLAG_ULP_STOPPED)) {
                bnxt_close_nic(bp, true, false);
                bnxt_open_nic(bp, true, false);
        }
@@ -266,6 +266,7 @@ void bnxt_ulp_stop(struct bnxt *bp)
        if (!edev)
                return;
 
+       edev->flags |= BNXT_EN_FLAG_ULP_STOPPED;
        for (i = 0; i < BNXT_MAX_ULP; i++) {
                struct bnxt_ulp *ulp = &edev->ulp_tbl[i];
 
@@ -276,7 +277,7 @@ void bnxt_ulp_stop(struct bnxt *bp)
        }
 }
 
-void bnxt_ulp_start(struct bnxt *bp)
+void bnxt_ulp_start(struct bnxt *bp, int err)
 {
        struct bnxt_en_dev *edev = bp->edev;
        struct bnxt_ulp_ops *ops;
@@ -285,6 +286,11 @@ void bnxt_ulp_start(struct bnxt *bp)
        if (!edev)
                return;
 
+       edev->flags &= ~BNXT_EN_FLAG_ULP_STOPPED;
+
+       if (err)
+               return;
+
        for (i = 0; i < BNXT_MAX_ULP; i++) {
                struct bnxt_ulp *ulp = &edev->ulp_tbl[i];
 
index cd78453..9895406 100644 (file)
@@ -64,6 +64,7 @@ struct bnxt_en_dev {
        #define BNXT_EN_FLAG_ROCE_CAP           (BNXT_EN_FLAG_ROCEV1_CAP | \
                                                 BNXT_EN_FLAG_ROCEV2_CAP)
        #define BNXT_EN_FLAG_MSIX_REQUESTED     0x4
+       #define BNXT_EN_FLAG_ULP_STOPPED        0x8
        const struct bnxt_en_ops        *en_ops;
        struct bnxt_ulp                 ulp_tbl[BNXT_MAX_ULP];
 };
@@ -92,7 +93,7 @@ int bnxt_get_ulp_msix_num(struct bnxt *bp);
 int bnxt_get_ulp_msix_base(struct bnxt *bp);
 int bnxt_get_ulp_stat_ctxs(struct bnxt *bp);
 void bnxt_ulp_stop(struct bnxt *bp);
-void bnxt_ulp_start(struct bnxt *bp);
+void bnxt_ulp_start(struct bnxt *bp, int err);
 void bnxt_ulp_sriov_cfg(struct bnxt *bp, int num_vfs);
 void bnxt_ulp_shutdown(struct bnxt *bp);
 void bnxt_ulp_irq_stop(struct bnxt *bp);