vmxnet3: Changes for vmxnet3 adapter version 2 (fwd)
authorShreyas Bhatewara <sbhatewara@vmware.com>
Fri, 19 Jun 2015 20:38:29 +0000 (13:38 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 23 Jun 2015 13:26:01 +0000 (06:26 -0700)
Make the driver understand adapter version 2.

Cc: Rachel Lunnon <rachel_lunnon@stormagic.com>
Signed-off-by: Guolin Yang <gyang@vmware.com>
Signed-off-by: Shreyas N Bhatewara <sbhatewara@vmware.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/vmxnet3/vmxnet3_defs.h
drivers/net/vmxnet3/vmxnet3_drv.c
drivers/net/vmxnet3/vmxnet3_int.h

index 3718d02..221a530 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Linux driver for VMware's vmxnet3 ethernet NIC.
  *
- * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
+ * Copyright (C) 2008-2015, VMware, Inc. All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -277,6 +277,40 @@ struct Vmxnet3_RxCompDesc {
 #endif  /* __BIG_ENDIAN_BITFIELD */
 };
 
+struct Vmxnet3_RxCompDescExt {
+       __le32          dword1;
+       u8              segCnt;       /* Number of aggregated packets */
+       u8              dupAckCnt;    /* Number of duplicate Acks */
+       __le16          tsDelta;      /* TCP timestamp difference */
+       __le32          dword2;
+#ifdef __BIG_ENDIAN_BITFIELD
+       u32             gen:1;        /* generation bit */
+       u32             type:7;       /* completion type */
+       u32             fcs:1;        /* Frame CRC correct */
+       u32             frg:1;        /* IP Fragment */
+       u32             v4:1;         /* IPv4 */
+       u32             v6:1;         /* IPv6 */
+       u32             ipc:1;        /* IP Checksum Correct */
+       u32             tcp:1;        /* TCP packet */
+       u32             udp:1;        /* UDP packet */
+       u32             tuc:1;        /* TCP/UDP Checksum Correct */
+       u32             mss:16;
+#else
+       u32             mss:16;
+       u32             tuc:1;        /* TCP/UDP Checksum Correct */
+       u32             udp:1;        /* UDP packet */
+       u32             tcp:1;        /* TCP packet */
+       u32             ipc:1;        /* IP Checksum Correct */
+       u32             v6:1;         /* IPv6 */
+       u32             v4:1;         /* IPv4 */
+       u32             frg:1;        /* IP Fragment */
+       u32             fcs:1;        /* Frame CRC correct */
+       u32             type:7;       /* completion type */
+       u32             gen:1;        /* generation bit */
+#endif  /* __BIG_ENDIAN_BITFIELD */
+};
+
+
 /* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */
 #define VMXNET3_RCD_TUC_SHIFT  16
 #define VMXNET3_RCD_IPC_SHIFT  19
@@ -310,6 +344,7 @@ union Vmxnet3_GenericDesc {
        struct Vmxnet3_RxDesc           rxd;
        struct Vmxnet3_TxCompDesc       tcd;
        struct Vmxnet3_RxCompDesc       rcd;
+       struct Vmxnet3_RxCompDescExt    rcdExt;
 };
 
 #define VMXNET3_INIT_GEN       1
@@ -361,6 +396,7 @@ enum {
 /* completion descriptor types */
 #define VMXNET3_CDTYPE_TXCOMP      0    /* Tx Completion Descriptor */
 #define VMXNET3_CDTYPE_RXCOMP      3    /* Rx Completion Descriptor */
+#define VMXNET3_CDTYPE_RXCOMP_LRO  4    /* Rx Completion Descriptor for LRO */
 
 enum {
        VMXNET3_GOS_BITS_UNK    = 0,   /* unknown */
index ab53975..da11bb5 100644 (file)
@@ -1163,6 +1163,52 @@ vmxnet3_rx_error(struct vmxnet3_rx_queue *rq, struct Vmxnet3_RxCompDesc *rcd,
 }
 
 
+static u32
+vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb,
+                   union Vmxnet3_GenericDesc *gdesc)
+{
+       u32 hlen, maplen;
+       union {
+               void *ptr;
+               struct ethhdr *eth;
+               struct iphdr *ipv4;
+               struct ipv6hdr *ipv6;
+               struct tcphdr *tcp;
+       } hdr;
+       BUG_ON(gdesc->rcd.tcp == 0);
+
+       maplen = skb_headlen(skb);
+       if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen))
+               return 0;
+
+       hdr.eth = eth_hdr(skb);
+       if (gdesc->rcd.v4) {
+               BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP));
+               hdr.ptr += sizeof(struct ethhdr);
+               BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP);
+               hlen = hdr.ipv4->ihl << 2;
+               hdr.ptr += hdr.ipv4->ihl << 2;
+       } else if (gdesc->rcd.v6) {
+               BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6));
+               hdr.ptr += sizeof(struct ethhdr);
+               /* Use an estimated value, since we also need to handle
+                * TSO case.
+                */
+               if (hdr.ipv6->nexthdr != IPPROTO_TCP)
+                       return sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
+               hlen = sizeof(struct ipv6hdr);
+               hdr.ptr += sizeof(struct ipv6hdr);
+       } else {
+               /* Non-IP pkt, dont estimate header length */
+               return 0;
+       }
+
+       if (hlen + sizeof(struct tcphdr) > maplen)
+               return 0;
+
+       return (hlen + (hdr.tcp->doff << 2));
+}
+
 static int
 vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
                       struct vmxnet3_adapter *adapter, int quota)
@@ -1174,6 +1220,7 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
        bool skip_page_frags = false;
        struct Vmxnet3_RxCompDesc *rcd;
        struct vmxnet3_rx_ctx *ctx = &rq->rx_ctx;
+       u16 segCnt = 0, mss = 0;
 #ifdef __BIG_ENDIAN_BITFIELD
        struct Vmxnet3_RxDesc rxCmdDesc;
        struct Vmxnet3_RxCompDesc rxComp;
@@ -1262,7 +1309,19 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
                                                       PCI_DMA_FROMDEVICE);
                        rxd->addr = cpu_to_le64(rbi->dma_addr);
                        rxd->len = rbi->len;
-
+                       if (adapter->version == 2 &&
+                           rcd->type == VMXNET3_CDTYPE_RXCOMP_LRO) {
+                               struct Vmxnet3_RxCompDescExt *rcdlro;
+                               rcdlro = (struct Vmxnet3_RxCompDescExt *)rcd;
+
+                               segCnt = rcdlro->segCnt;
+                               BUG_ON(segCnt <= 1);
+                               mss = rcdlro->mss;
+                               if (unlikely(segCnt <= 1))
+                                       segCnt = 0;
+                       } else {
+                               segCnt = 0;
+                       }
                } else {
                        BUG_ON(ctx->skb == NULL && !skip_page_frags);
 
@@ -1311,12 +1370,40 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
 
                skb = ctx->skb;
                if (rcd->eop) {
+                       u32 mtu = adapter->netdev->mtu;
                        skb->len += skb->data_len;
 
                        vmxnet3_rx_csum(adapter, skb,
                                        (union Vmxnet3_GenericDesc *)rcd);
                        skb->protocol = eth_type_trans(skb, adapter->netdev);
-
+                       if (!rcd->tcp || !adapter->lro)
+                               goto not_lro;
+
+                       if (segCnt != 0 && mss != 0) {
+                               skb_shinfo(skb)->gso_type = rcd->v4 ?
+                                       SKB_GSO_TCPV4 : SKB_GSO_TCPV6;
+                               skb_shinfo(skb)->gso_size = mss;
+                               skb_shinfo(skb)->gso_segs = segCnt;
+                       } else if (segCnt != 0 || skb->len > mtu) {
+                               u32 hlen;
+
+                               hlen = vmxnet3_get_hdr_len(adapter, skb,
+                                       (union Vmxnet3_GenericDesc *)rcd);
+                               if (hlen == 0)
+                                       goto not_lro;
+
+                               skb_shinfo(skb)->gso_type =
+                                       rcd->v4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6;
+                               if (segCnt != 0) {
+                                       skb_shinfo(skb)->gso_segs = segCnt;
+                                       skb_shinfo(skb)->gso_size =
+                                               DIV_ROUND_UP(skb->len -
+                                                       hlen, segCnt);
+                               } else {
+                                       skb_shinfo(skb)->gso_size = mtu - hlen;
+                               }
+                       }
+not_lro:
                        if (unlikely(rcd->ts))
                                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rcd->tci);
 
@@ -3041,14 +3128,19 @@ vmxnet3_probe_device(struct pci_dev *pdev,
                goto err_alloc_pci;
 
        ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS);
-       if (ver & 1) {
+       if (ver & 2) {
+               VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_VRRS, 2);
+               adapter->version = 2;
+       } else if (ver & 1) {
                VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_VRRS, 1);
+               adapter->version = 1;
        } else {
                dev_err(&pdev->dev,
                        "Incompatible h/w version (0x%x) for adapter\n", ver);
                err = -EBUSY;
                goto err_ver;
        }
+       dev_dbg(&pdev->dev, "Using device version %d\n", adapter->version);
 
        ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS);
        if (ver & 1) {
index 6bb769a..e9f1075 100644 (file)
@@ -328,6 +328,10 @@ struct vmxnet3_adapter {
 
        u8                      __iomem *hw_addr0; /* for BAR 0 */
        u8                      __iomem *hw_addr1; /* for BAR 1 */
+       u8                              version;
+
+       bool                            rxcsum;
+       bool                            lro;
 
 #ifdef VMXNET3_RSS
        struct UPT1_RSSConf             *rss_conf;