diff options
author | Shreyas Bhatewara <sbhatewara@vmware.com> | 2015-06-19 22:38:29 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-06-23 15:26:01 +0200 |
commit | 45dac1d6ea045ae56e4df8d9c70c92c7412bd4fc (patch) | |
tree | 6860f6148378c225f224139c30b41e1bbfd79fb8 /drivers/net/vmxnet3/vmxnet3_drv.c | |
parent | vmxnet3: Fix memory leaks in rx path (fwd) (diff) | |
download | linux-45dac1d6ea045ae56e4df8d9c70c92c7412bd4fc.tar.xz linux-45dac1d6ea045ae56e4df8d9c70c92c7412bd4fc.zip |
vmxnet3: Changes for vmxnet3 adapter version 2 (fwd)
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>
Diffstat (limited to 'drivers/net/vmxnet3/vmxnet3_drv.c')
-rw-r--r-- | drivers/net/vmxnet3/vmxnet3_drv.c | 98 |
1 files changed, 95 insertions, 3 deletions
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index ab539758bec1..da11bb5e9c7f 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -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) { |