bnxt_en: Add support for L2 rewrite
authorVenkat Duvvuru <venkatkumar.duvvuru@broadcom.com>
Thu, 31 Oct 2019 05:07:45 +0000 (01:07 -0400)
committerDavid S. Miller <davem@davemloft.net>
Thu, 31 Oct 2019 21:48:11 +0000 (14:48 -0700)
This patch adds support for packet edit offload of L2 fields (src mac &
dst mac, also referred as L2 rewrite). Only when the mask is fully exact
match for a field, the command is sent down to the adapter to offload
such a flow. Otherwise, an error is returned.

v2: Fix pointer alignment issue in bnxt_fill_l2_rewrite_fields() [MChan]

Signed-off-by: Venkat Duvvuru <venkatkumar.duvvuru@broadcom.com>
Signed-off-by: Michael Chan <michael.chan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c
drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h

index c8062d0..80b39ff 100644 (file)
@@ -16,6 +16,7 @@
 #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 "bnxt_hsi.h"
@@ -36,6 +37,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 +114,115 @@ static int bnxt_tc_parse_tunnel_set(struct bnxt *bp,
        return 0;
 }
 
+/* Key & Mask from the stack comes unaligned in multiple iterations.
+ * 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, u8 *eth_addr,
+                   u8 *eth_addr_mask)
+{
+       u32 mask, val, offset;
+       u8 htype;
+
+       offset = act->mangle.offset;
+       htype = act->mangle.htype;
+       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;
+               mask = ~act->mangle.mask;
+               val = act->mangle.val;
+
+               bnxt_set_l2_key_mask(val, mask, &eth_addr[offset],
+                                    &eth_addr_mask[offset]);
+               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 +256,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,
+                                                (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 +524,15 @@ 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_TUNNEL_DECAP ||
            actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) {
                req.tunnel_handle = tunnel_handle;
index 4f05305..6d0d485 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,7 @@ 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)
 
        u16                             dst_fid;
        struct net_device               *dst_dev;
@@ -79,6 +86,10 @@ 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 bnxt_tc_flow {