bnxt_en: Add GRO logic for BCM5731X chips.
authorMichael Chan <michael.chan@broadcom.com>
Mon, 13 Jun 2016 06:25:35 +0000 (02:25 -0400)
committerDavid S. Miller <davem@davemloft.net>
Tue, 14 Jun 2016 23:16:13 +0000 (19:16 -0400)
Add bnxt_gro_func_5731x() to handle GRO packets for this chip.  The
completion structures used in the new chip have new data to help determine
the header offsets.  The offsets can be off by 4 if the packet is an
internal loopback packet (e.g. from one VF to another VF).  Some additional
logic is added to adjust the offsets if it is a loopback packet.

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

index 4e3b9f5295a61977860a07cdd04f9ce9e4c6521b..5f9285c2bad91d1bb0ad40f9bd7b477bb98435b2 100644 (file)
@@ -924,6 +924,7 @@ static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
        }
        tpa_info->flags2 = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_flags2);
        tpa_info->metadata = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_metadata);
+       tpa_info->hdr_info = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_hdr_info);
 
        rxr->rx_prod = NEXT_RX(prod);
        cons = NEXT_RX(cons);
@@ -942,6 +943,90 @@ static void bnxt_abort_tpa(struct bnxt *bp, struct bnxt_napi *bnapi,
                bnxt_reuse_rx_agg_bufs(bnapi, cp_cons, agg_bufs);
 }
 
+static struct sk_buff *bnxt_gro_func_5731x(struct bnxt_tpa_info *tpa_info,
+                                          int payload_off, int tcp_ts,
+                                          struct sk_buff *skb)
+{
+#ifdef CONFIG_INET
+       struct tcphdr *th;
+       int len, nw_off;
+       u16 outer_ip_off, inner_ip_off, inner_mac_off;
+       u32 hdr_info = tpa_info->hdr_info;
+       bool loopback = false;
+
+       inner_ip_off = BNXT_TPA_INNER_L3_OFF(hdr_info);
+       inner_mac_off = BNXT_TPA_INNER_L2_OFF(hdr_info);
+       outer_ip_off = BNXT_TPA_OUTER_L3_OFF(hdr_info);
+
+       /* If the packet is an internal loopback packet, the offsets will
+        * have an extra 4 bytes.
+        */
+       if (inner_mac_off == 4) {
+               loopback = true;
+       } else if (inner_mac_off > 4) {
+               __be16 proto = *((__be16 *)(skb->data + inner_ip_off -
+                                           ETH_HLEN - 2));
+
+               /* We only support inner iPv4/ipv6.  If we don't see the
+                * correct protocol ID, it must be a loopback packet where
+                * the offsets are off by 4.
+                */
+               if (proto != htons(ETH_P_IP) && proto && htons(ETH_P_IPV6))
+                       loopback = true;
+       }
+       if (loopback) {
+               /* internal loopback packet, subtract all offsets by 4 */
+               inner_ip_off -= 4;
+               inner_mac_off -= 4;
+               outer_ip_off -= 4;
+       }
+
+       nw_off = inner_ip_off - ETH_HLEN;
+       skb_set_network_header(skb, nw_off);
+       if (tpa_info->flags2 & RX_TPA_START_CMP_FLAGS2_IP_TYPE) {
+               struct ipv6hdr *iph = ipv6_hdr(skb);
+
+               skb_set_transport_header(skb, nw_off + sizeof(struct ipv6hdr));
+               len = skb->len - skb_transport_offset(skb);
+               th = tcp_hdr(skb);
+               th->check = ~tcp_v6_check(len, &iph->saddr, &iph->daddr, 0);
+       } else {
+               struct iphdr *iph = ip_hdr(skb);
+
+               skb_set_transport_header(skb, nw_off + sizeof(struct iphdr));
+               len = skb->len - skb_transport_offset(skb);
+               th = tcp_hdr(skb);
+               th->check = ~tcp_v4_check(len, iph->saddr, iph->daddr, 0);
+       }
+
+       if (inner_mac_off) { /* tunnel */
+               struct udphdr *uh = NULL;
+               __be16 proto = *((__be16 *)(skb->data + outer_ip_off -
+                                           ETH_HLEN - 2));
+
+               if (proto == htons(ETH_P_IP)) {
+                       struct iphdr *iph = (struct iphdr *)skb->data;
+
+                       if (iph->protocol == IPPROTO_UDP)
+                               uh = (struct udphdr *)(iph + 1);
+               } else {
+                       struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;
+
+                       if (iph->nexthdr == IPPROTO_UDP)
+                               uh = (struct udphdr *)(iph + 1);
+               }
+               if (uh) {
+                       if (uh->check)
+                               skb_shinfo(skb)->gso_type |=
+                                       SKB_GSO_UDP_TUNNEL_CSUM;
+                       else
+                               skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
+               }
+       }
+#endif
+       return skb;
+}
+
 #define BNXT_IPV4_HDR_SIZE     (sizeof(struct iphdr) + sizeof(struct tcphdr))
 #define BNXT_IPV6_HDR_SIZE     (sizeof(struct ipv6hdr) + sizeof(struct tcphdr))
 
@@ -2283,7 +2368,7 @@ static void bnxt_set_tpa_flags(struct bnxt *bp)
        bp->flags &= ~BNXT_FLAG_TPA;
        if (bp->dev->features & NETIF_F_LRO)
                bp->flags |= BNXT_FLAG_LRO;
-       if ((bp->dev->features & NETIF_F_GRO) && (bp->pdev->revision > 0))
+       if (bp->dev->features & NETIF_F_GRO)
                bp->flags |= BNXT_FLAG_GRO;
 }
 
@@ -6433,6 +6518,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto init_err;
 
        bp->gro_func = bnxt_gro_func_5730x;
+       if (BNXT_CHIP_NUM_57X1X(bp->chip_num))
+               bp->gro_func = bnxt_gro_func_5731x;
 
        rc = bnxt_hwrm_func_drv_rgtr(bp);
        if (rc)
index d62f4c2039f8d23eb52f801df84a39fe48d3c7b4..c1b41fb216dc9ee8530759e29ba18cf1b2d3c8ed 100644 (file)
@@ -298,13 +298,14 @@ struct rx_tpa_start_cmp_ext {
        #define RX_TPA_START_CMP_FLAGS2_L4_CS_CALC              (0x1 << 1)
        #define RX_TPA_START_CMP_FLAGS2_T_IP_CS_CALC            (0x1 << 2)
        #define RX_TPA_START_CMP_FLAGS2_T_L4_CS_CALC            (0x1 << 3)
+       #define RX_TPA_START_CMP_FLAGS2_IP_TYPE                 (0x1 << 8)
 
        __le32 rx_tpa_start_cmp_metadata;
        __le32 rx_tpa_start_cmp_cfa_code_v2;
        #define RX_TPA_START_CMP_V2                             (0x1 << 0)
        #define RX_TPA_START_CMP_CFA_CODE                       (0xffff << 16)
         #define RX_TPA_START_CMPL_CFA_CODE_SHIFT                16
-       __le32 rx_tpa_start_cmp_unused5;
+       __le32 rx_tpa_start_cmp_hdr_info;
 };
 
 struct rx_tpa_end_cmp {
@@ -584,6 +585,19 @@ struct bnxt_tpa_info {
        u32                     metadata;
        enum pkt_hash_types     hash_type;
        u32                     rss_hash;
+       u32                     hdr_info;
+
+#define BNXT_TPA_L4_SIZE(hdr_info)     \
+       (((hdr_info) & 0xf8000000) ? ((hdr_info) >> 27) : 32)
+
+#define BNXT_TPA_INNER_L3_OFF(hdr_info)        \
+       (((hdr_info) >> 18) & 0x1ff)
+
+#define BNXT_TPA_INNER_L2_OFF(hdr_info)        \
+       (((hdr_info) >> 9) & 0x1ff)
+
+#define BNXT_TPA_OUTER_L3_OFF(hdr_info)        \
+       ((hdr_info) & 0x1ff)
 };
 
 struct bnxt_rx_ring_info {