These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / net / vmxnet3 / vmxnet3_drv.c
index 61c0840..0cbf520 100644 (file)
@@ -587,6 +587,12 @@ vmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx,
                                                &adapter->pdev->dev,
                                                rbi->skb->data, rbi->len,
                                                PCI_DMA_FROMDEVICE);
+                               if (dma_mapping_error(&adapter->pdev->dev,
+                                                     rbi->dma_addr)) {
+                                       dev_kfree_skb_any(rbi->skb);
+                                       rq->stats.rx_buf_alloc_failure++;
+                                       break;
+                               }
                        } else {
                                /* rx buffer skipped by the device */
                        }
@@ -605,13 +611,18 @@ vmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx,
                                                &adapter->pdev->dev,
                                                rbi->page, 0, PAGE_SIZE,
                                                PCI_DMA_FROMDEVICE);
+                               if (dma_mapping_error(&adapter->pdev->dev,
+                                                     rbi->dma_addr)) {
+                                       put_page(rbi->page);
+                                       rq->stats.rx_buf_alloc_failure++;
+                                       break;
+                               }
                        } else {
                                /* rx buffers skipped by the device */
                        }
                        val = VMXNET3_RXD_BTYPE_BODY << VMXNET3_RXD_BTYPE_SHIFT;
                }
 
-               BUG_ON(rbi->dma_addr == 0);
                gd->rxd.addr = cpu_to_le64(rbi->dma_addr);
                gd->dword[2] = cpu_to_le32((!ring->gen << VMXNET3_RXD_GEN_SHIFT)
                                           | val | rbi->len);
@@ -655,7 +666,7 @@ vmxnet3_append_frag(struct sk_buff *skb, struct Vmxnet3_RxCompDesc *rcd,
 }
 
 
-static void
+static int
 vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
                struct vmxnet3_tx_queue *tq, struct pci_dev *pdev,
                struct vmxnet3_adapter *adapter)
@@ -715,6 +726,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
                tbi->dma_addr = dma_map_single(&adapter->pdev->dev,
                                skb->data + buf_offset, buf_size,
                                PCI_DMA_TODEVICE);
+               if (dma_mapping_error(&adapter->pdev->dev, tbi->dma_addr))
+                       return -EFAULT;
 
                tbi->len = buf_size;
 
@@ -755,6 +768,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
                        tbi->dma_addr = skb_frag_dma_map(&adapter->pdev->dev, frag,
                                                         buf_offset, buf_size,
                                                         DMA_TO_DEVICE);
+                       if (dma_mapping_error(&adapter->pdev->dev, tbi->dma_addr))
+                               return -EFAULT;
 
                        tbi->len = buf_size;
 
@@ -782,6 +797,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
        /* set the last buf_info for the pkt */
        tbi->skb = skb;
        tbi->sop_idx = ctx->sop_txd - tq->tx_ring.base;
+
+       return 0;
 }
 
 
@@ -861,6 +878,9 @@ vmxnet3_parse_and_copy_hdr(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
                                             , skb_headlen(skb));
                }
 
+               if (skb->len <= VMXNET3_HDR_COPY_SIZE)
+                       ctx->copy_size = skb->len;
+
                /* make sure headers are accessible directly */
                if (unlikely(!pskb_may_pull(skb, ctx->copy_size)))
                        goto err;
@@ -1017,7 +1037,8 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
        }
 
        /* fill tx descs related to addr & len */
-       vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter);
+       if (vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter))
+               goto unlock_drop_pkt;
 
        /* setup the EOP desc */
        ctx.eop_txd->dword[3] = cpu_to_le32(VMXNET3_TXD_CQ | VMXNET3_TXD_EOP);
@@ -1160,6 +1181,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)
@@ -1167,10 +1234,11 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
        static const u32 rxprod_reg[2] = {
                VMXNET3_REG_RXPROD, VMXNET3_REG_RXPROD2
        };
-       u32 num_rxd = 0;
+       u32 num_pkts = 0;
        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;
@@ -1181,17 +1249,17 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
                struct vmxnet3_rx_buf_info *rbi;
                struct sk_buff *skb, *new_skb = NULL;
                struct page *new_page = NULL;
+               dma_addr_t new_dma_addr;
                int num_to_alloc;
                struct Vmxnet3_RxDesc *rxd;
                u32 idx, ring_idx;
                struct vmxnet3_cmd_ring *ring = NULL;
-               if (num_rxd >= quota) {
+               if (num_pkts >= quota) {
                        /* we may stop even before we see the EOP desc of
                         * the current pkt
                         */
                        break;
                }
-               num_rxd++;
                BUG_ON(rcd->rqID != rq->qid && rcd->rqID != rq->qid2);
                idx = rcd->rxdIdx;
                ring_idx = rcd->rqID < adapter->num_rx_queues ? 0 : 1;
@@ -1238,6 +1306,21 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
                                skip_page_frags = true;
                                goto rcd_done;
                        }
+                       new_dma_addr = dma_map_single(&adapter->pdev->dev,
+                                                     new_skb->data, rbi->len,
+                                                     PCI_DMA_FROMDEVICE);
+                       if (dma_mapping_error(&adapter->pdev->dev,
+                                             new_dma_addr)) {
+                               dev_kfree_skb(new_skb);
+                               /* Skb allocation failed, do not handover this
+                                * skb to stack. Reuse it. Drop the existing pkt
+                                */
+                               rq->stats.rx_buf_alloc_failure++;
+                               ctx->skb = NULL;
+                               rq->stats.drop_total++;
+                               skip_page_frags = true;
+                               goto rcd_done;
+                       }
 
                        dma_unmap_single(&adapter->pdev->dev, rbi->dma_addr,
                                         rbi->len,
@@ -1254,12 +1337,22 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
 
                        /* Immediate refill */
                        rbi->skb = new_skb;
-                       rbi->dma_addr = dma_map_single(&adapter->pdev->dev,
-                                                      rbi->skb->data, rbi->len,
-                                                      PCI_DMA_FROMDEVICE);
+                       rbi->dma_addr = new_dma_addr;
                        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);
 
@@ -1273,47 +1366,85 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
                        if (skip_page_frags)
                                goto rcd_done;
 
-                       new_page = alloc_page(GFP_ATOMIC);
-                       if (unlikely(new_page == NULL)) {
+                       if (rcd->len) {
+                               new_page = alloc_page(GFP_ATOMIC);
                                /* Replacement page frag could not be allocated.
                                 * Reuse this page. Drop the pkt and free the
                                 * skb which contained this page as a frag. Skip
                                 * processing all the following non-sop frags.
                                 */
-                               rq->stats.rx_buf_alloc_failure++;
-                               dev_kfree_skb(ctx->skb);
-                               ctx->skb = NULL;
-                               skip_page_frags = true;
-                               goto rcd_done;
-                       }
+                               if (unlikely(!new_page)) {
+                                       rq->stats.rx_buf_alloc_failure++;
+                                       dev_kfree_skb(ctx->skb);
+                                       ctx->skb = NULL;
+                                       skip_page_frags = true;
+                                       goto rcd_done;
+                               }
+                               new_dma_addr = dma_map_page(&adapter->pdev->dev,
+                                                           new_page,
+                                                           0, PAGE_SIZE,
+                                                           PCI_DMA_FROMDEVICE);
+                               if (dma_mapping_error(&adapter->pdev->dev,
+                                                     new_dma_addr)) {
+                                       put_page(new_page);
+                                       rq->stats.rx_buf_alloc_failure++;
+                                       dev_kfree_skb(ctx->skb);
+                                       ctx->skb = NULL;
+                                       skip_page_frags = true;
+                                       goto rcd_done;
+                               }
 
-                       if (rcd->len) {
                                dma_unmap_page(&adapter->pdev->dev,
                                               rbi->dma_addr, rbi->len,
                                               PCI_DMA_FROMDEVICE);
 
                                vmxnet3_append_frag(ctx->skb, rcd, rbi);
-                       }
 
-                       /* Immediate refill */
-                       rbi->page = new_page;
-                       rbi->dma_addr = dma_map_page(&adapter->pdev->dev,
-                                                    rbi->page,
-                                                    0, PAGE_SIZE,
-                                                    PCI_DMA_FROMDEVICE);
-                       rxd->addr = cpu_to_le64(rbi->dma_addr);
-                       rxd->len = rbi->len;
+                               /* Immediate refill */
+                               rbi->page = new_page;
+                               rbi->dma_addr = new_dma_addr;
+                               rxd->addr = cpu_to_le64(rbi->dma_addr);
+                               rxd->len = rbi->len;
+                       }
                }
 
 
                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);
 
@@ -1323,6 +1454,7 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
                                napi_gro_receive(&rq->napi, skb);
 
                        ctx->skb = NULL;
+                       num_pkts++;
                }
 
 rcd_done:
@@ -1353,7 +1485,7 @@ rcd_done:
                                  &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, &rxComp);
        }
 
-       return num_rxd;
+       return num_pkts;
 }
 
 
@@ -2067,16 +2199,18 @@ vmxnet3_set_mc(struct net_device *netdev)
                if (!netdev_mc_empty(netdev)) {
                        new_table = vmxnet3_copy_mc(netdev);
                        if (new_table) {
-                               rxConf->mfTableLen = cpu_to_le16(
-                                       netdev_mc_count(netdev) * ETH_ALEN);
+                               size_t sz = netdev_mc_count(netdev) * ETH_ALEN;
+
+                               rxConf->mfTableLen = cpu_to_le16(sz);
                                new_table_pa = dma_map_single(
                                                        &adapter->pdev->dev,
                                                        new_table,
-                                                       rxConf->mfTableLen,
+                                                       sz,
                                                        PCI_DMA_TODEVICE);
                        }
 
-                       if (new_table_pa) {
+                       if (!dma_mapping_error(&adapter->pdev->dev,
+                                              new_table_pa)) {
                                new_mode |= VMXNET3_RXM_MCAST;
                                rxConf->mfTablePA = cpu_to_le64(new_table_pa);
                        } else {
@@ -2984,6 +3118,11 @@ vmxnet3_probe_device(struct pci_dev *pdev,
        adapter->adapter_pa = dma_map_single(&adapter->pdev->dev, adapter,
                                             sizeof(struct vmxnet3_adapter),
                                             PCI_DMA_TODEVICE);
+       if (dma_mapping_error(&adapter->pdev->dev, adapter->adapter_pa)) {
+               dev_err(&pdev->dev, "Failed to map dma\n");
+               err = -EFAULT;
+               goto err_dma_map;
+       }
        adapter->shared = dma_alloc_coherent(
                                &adapter->pdev->dev,
                                sizeof(struct Vmxnet3_DriverShared),
@@ -3038,14 +3177,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) {
@@ -3137,6 +3281,7 @@ err_alloc_queue_desc:
 err_alloc_shared:
        dma_unmap_single(&adapter->pdev->dev, adapter->adapter_pa,
                         sizeof(struct vmxnet3_adapter), PCI_DMA_TODEVICE);
+err_dma_map:
        free_netdev(netdev);
        return err;
 }
@@ -3184,6 +3329,32 @@ vmxnet3_remove_device(struct pci_dev *pdev)
        free_netdev(netdev);
 }
 
+static void vmxnet3_shutdown_device(struct pci_dev *pdev)
+{
+       struct net_device *netdev = pci_get_drvdata(pdev);
+       struct vmxnet3_adapter *adapter = netdev_priv(netdev);
+       unsigned long flags;
+
+       /* Reset_work may be in the middle of resetting the device, wait for its
+        * completion.
+        */
+       while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state))
+               msleep(1);
+
+       if (test_and_set_bit(VMXNET3_STATE_BIT_QUIESCED,
+                            &adapter->state)) {
+               clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state);
+               return;
+       }
+       spin_lock_irqsave(&adapter->cmd_lock, flags);
+       VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
+                              VMXNET3_CMD_QUIESCE_DEV);
+       spin_unlock_irqrestore(&adapter->cmd_lock, flags);
+       vmxnet3_disable_all_intrs(adapter);
+
+       clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state);
+}
+
 
 #ifdef CONFIG_PM
 
@@ -3360,6 +3531,7 @@ static struct pci_driver vmxnet3_driver = {
        .id_table       = vmxnet3_pciid_table,
        .probe          = vmxnet3_probe_device,
        .remove         = vmxnet3_remove_device,
+       .shutdown       = vmxnet3_shutdown_device,
 #ifdef CONFIG_PM
        .driver.pm      = &vmxnet3_pm_ops,
 #endif