bmips: fix Tx cleanup when NAPI poll budget is zero
[openwrt/staging/noltari.git] / target / linux / bmips / files / drivers / net / ethernet / broadcom / bcm6368-enetsw.c
index 7ddbf8a10afc34538617ee3cb246b71fb9ba0b7a..c655ae8d27753106298d0bae151c98444bbab337 100644 (file)
@@ -25,6 +25,8 @@
 #define ENETSW_TAG_SIZE                        6
 #define ENETSW_MTU_OVERHEAD            (VLAN_ETH_HLEN + VLAN_HLEN + \
                                         ENETSW_TAG_SIZE)
+#define ENETSW_FRAG_SIZE(x)            (SKB_DATA_ALIGN(NET_SKB_PAD + x + \
+                                        SKB_DATA_ALIGN(sizeof(struct skb_shared_info))))
 
 /* default number of descriptor */
 #define ENETSW_DEF_RX_DESC             64
@@ -170,13 +172,16 @@ struct bcm6368_enetsw {
        /* next dirty rx descriptor to refill */
        int rx_dirty_desc;
 
-       /* size of allocated rx skbs */
-       unsigned int rx_skb_size;
+       /* size of allocated rx buffer */
+       unsigned int rx_buf_size;
 
-       /* list of skb given to hw for rx */
-       struct sk_buff **rx_skb;
+       /* size of allocated rx frag */
+       unsigned int rx_frag_size;
 
-       /* used when rx skb allocation failed, so we defer rx queue
+       /* list of buffer given to hw for rx */
+       unsigned char **rx_buf;
+
+       /* used when rx buffer allocation failed, so we defer rx queue
         * refill */
        struct timer_list rx_timeout;
 
@@ -251,32 +256,37 @@ static inline void dmas_writel(struct bcm6368_enetsw *priv, u32 val,
 /*
  * refill rx queue
  */
-static int bcm6368_enetsw_refill_rx(struct net_device *dev)
+static int bcm6368_enetsw_refill_rx(struct net_device *dev, bool napi_mode)
 {
        struct bcm6368_enetsw *priv = netdev_priv(dev);
 
        while (priv->rx_desc_count < priv->rx_ring_size) {
                struct bcm6368_enetsw_desc *desc;
-               struct sk_buff *skb;
-               dma_addr_t p;
                int desc_idx;
                u32 len_stat;
 
                desc_idx = priv->rx_dirty_desc;
                desc = &priv->rx_desc_cpu[desc_idx];
 
-               if (!priv->rx_skb[desc_idx]) {
-                       skb = netdev_alloc_skb(dev, priv->rx_skb_size);
-                       if (!skb)
+               if (!priv->rx_buf[desc_idx]) {
+                       unsigned char *buf;
+
+                       if (likely(napi_mode))
+                               buf = napi_alloc_frag(priv->rx_frag_size);
+                       else
+                               buf = netdev_alloc_frag(priv->rx_frag_size);
+
+                       if (unlikely(!buf))
                                break;
-                       priv->rx_skb[desc_idx] = skb;
-                       p = dma_map_single(&priv->pdev->dev, skb->data,
-                                          priv->rx_skb_size,
-                                          DMA_FROM_DEVICE);
-                       desc->address = p;
+
+                       priv->rx_buf[desc_idx] = buf;
+                       desc->address = dma_map_single(&priv->pdev->dev,
+                                                      buf + NET_SKB_PAD,
+                                                      priv->rx_buf_size,
+                                                      DMA_FROM_DEVICE);
                }
 
-               len_stat = priv->rx_skb_size << DMADESC_LENGTH_SHIFT;
+               len_stat = priv->rx_buf_size << DMADESC_LENGTH_SHIFT;
                len_stat |= DMADESC_OWNER_MASK;
                if (priv->rx_dirty_desc == priv->rx_ring_size - 1) {
                        len_stat |= DMADESC_WRAP_MASK;
@@ -313,7 +323,7 @@ static void bcm6368_enetsw_refill_rx_timer(struct timer_list *t)
        struct net_device *dev = priv->net_dev;
 
        spin_lock(&priv->rx_lock);
-       bcm6368_enetsw_refill_rx(dev);
+       bcm6368_enetsw_refill_rx(dev, false);
        spin_unlock(&priv->rx_lock);
 }
 
@@ -324,8 +334,12 @@ static int bcm6368_enetsw_receive_queue(struct net_device *dev, int budget)
 {
        struct bcm6368_enetsw *priv = netdev_priv(dev);
        struct device *kdev = &priv->pdev->dev;
+       struct list_head rx_list;
+       struct sk_buff *skb;
        int processed = 0;
 
+       INIT_LIST_HEAD(&rx_list);
+
        /* don't scan ring further than number of refilled
         * descriptor */
        if (budget > priv->rx_desc_count)
@@ -333,7 +347,8 @@ static int bcm6368_enetsw_receive_queue(struct net_device *dev, int budget)
 
        do {
                struct bcm6368_enetsw_desc *desc;
-               struct sk_buff *skb;
+               unsigned int frag_size;
+               unsigned char *buf;
                int desc_idx;
                u32 len_stat;
                unsigned int len;
@@ -355,7 +370,6 @@ static int bcm6368_enetsw_receive_queue(struct net_device *dev, int budget)
                priv->rx_curr_desc++;
                if (priv->rx_curr_desc == priv->rx_ring_size)
                        priv->rx_curr_desc = 0;
-               priv->rx_desc_count--;
 
                /* if the packet does not have start of packet _and_
                 * end of packet flag set, then just recycle it */
@@ -365,17 +379,17 @@ static int bcm6368_enetsw_receive_queue(struct net_device *dev, int budget)
                }
 
                /* valid packet */
-               skb = priv->rx_skb[desc_idx];
+               buf = priv->rx_buf[desc_idx];
                len = (len_stat & DMADESC_LENGTH_MASK)
                      >> DMADESC_LENGTH_SHIFT;
                /* don't include FCS */
                len -= 4;
 
                if (len < priv->copybreak) {
-                       struct sk_buff *nskb;
+                       unsigned int nfrag_size = ENETSW_FRAG_SIZE(len);
+                       unsigned char *nbuf = napi_alloc_frag(nfrag_size);
 
-                       nskb = netdev_alloc_skb(dev, len);
-                       if (!nskb) {
+                       if (unlikely(!nbuf)) {
                                /* forget packet, just rearm desc */
                                dev->stats.rx_dropped++;
                                continue;
@@ -383,25 +397,39 @@ static int bcm6368_enetsw_receive_queue(struct net_device *dev, int budget)
 
                        dma_sync_single_for_cpu(kdev, desc->address,
                                                len, DMA_FROM_DEVICE);
-                       memcpy(nskb->data, skb->data, len);
+                       memcpy(nbuf + NET_SKB_PAD, buf + NET_SKB_PAD, len);
                        dma_sync_single_for_device(kdev, desc->address,
                                                   len, DMA_FROM_DEVICE);
-                       skb = nskb;
+                       buf = nbuf;
+                       frag_size = nfrag_size;
                } else {
-                       dma_unmap_single(&priv->pdev->dev, desc->address,
-                                        priv->rx_skb_size, DMA_FROM_DEVICE);
-                       priv->rx_skb[desc_idx] = NULL;
+                       dma_unmap_single(kdev, desc->address,
+                                        priv->rx_buf_size, DMA_FROM_DEVICE);
+                       priv->rx_buf[desc_idx] = NULL;
+                       frag_size = priv->rx_frag_size;
                }
 
+               skb = napi_build_skb(buf, frag_size);
+               if (unlikely(!skb)) {
+                       skb_free_frag(buf);
+                       dev->stats.rx_dropped++;
+                       continue;
+               }
+
+               skb_reserve(skb, NET_SKB_PAD);
                skb_put(skb, len);
-               skb->protocol = eth_type_trans(skb, dev);
                dev->stats.rx_packets++;
                dev->stats.rx_bytes += len;
-               netif_receive_skb(skb);
-       } while (--budget > 0);
+               list_add_tail(&skb->list, &rx_list);
+       } while (processed < budget);
+
+       list_for_each_entry(skb, &rx_list, list)
+               skb->protocol = eth_type_trans(skb, dev);
+       netif_receive_skb_list(&rx_list);
+       priv->rx_desc_count -= processed;
 
        if (processed || !priv->rx_desc_count) {
-               bcm6368_enetsw_refill_rx(dev);
+               bcm6368_enetsw_refill_rx(dev, true);
 
                /* kick rx dma */
                dmac_writel(priv, priv->dma_chan_en_mask,
@@ -414,9 +442,11 @@ static int bcm6368_enetsw_receive_queue(struct net_device *dev, int budget)
 /*
  * try to or force reclaim of transmitted buffers
  */
-static int bcm6368_enetsw_tx_reclaim(struct net_device *dev, int force)
+static int bcm6368_enetsw_tx_reclaim(struct net_device *dev, int force,
+                                    int budget)
 {
        struct bcm6368_enetsw *priv = netdev_priv(dev);
+       unsigned int bytes = 0;
        int released = 0;
 
        while (priv->tx_desc_count < priv->tx_ring_size) {
@@ -453,10 +483,13 @@ static int bcm6368_enetsw_tx_reclaim(struct net_device *dev, int force)
                if (desc->len_stat & DMADESC_UNDER_MASK)
                        dev->stats.tx_errors++;
 
-               dev_kfree_skb(skb);
+               bytes += skb->len;
+               napi_consume_skb(skb, budget);
                released++;
        }
 
+       netdev_completed_queue(dev, released, bytes);
+
        if (netif_queue_stopped(dev) && released)
                netif_wake_queue(dev);
 
@@ -479,7 +512,7 @@ static int bcm6368_enetsw_poll(struct napi_struct *napi, int budget)
                         DMAC_IR_REG, priv->tx_chan);
 
        /* reclaim sent skb */
-       bcm6368_enetsw_tx_reclaim(dev, 0);
+       bcm6368_enetsw_tx_reclaim(dev, 0, budget);
 
        spin_lock(&priv->rx_lock);
        rx_work_done = bcm6368_enetsw_receive_queue(dev, budget);
@@ -589,6 +622,8 @@ bcm6368_enetsw_start_xmit(struct sk_buff *skb, struct net_device *dev)
        desc->len_stat = len_stat;
        wmb();
 
+       netdev_sent_queue(dev, skb->len);
+
        /* kick tx dma */
        dmac_writel(priv, priv->dma_chan_en_mask, DMAC_CHANCFG_REG,
                    priv->tx_chan);
@@ -680,7 +715,7 @@ static int bcm6368_enetsw_open(struct net_device *dev)
        priv->tx_skb = kzalloc(sizeof(struct sk_buff *) * priv->tx_ring_size,
                               GFP_KERNEL);
        if (!priv->tx_skb) {
-               dev_err(kdev, "cannot allocate rx skb queue\n");
+               dev_err(kdev, "cannot allocate tx skb queue\n");
                ret = -ENOMEM;
                goto out_free_tx_ring;
        }
@@ -690,11 +725,11 @@ static int bcm6368_enetsw_open(struct net_device *dev)
        priv->tx_curr_desc = 0;
        spin_lock_init(&priv->tx_lock);
 
-       /* init & fill rx ring with skbs */
-       priv->rx_skb = kzalloc(sizeof(struct sk_buff *) * priv->rx_ring_size,
+       /* init & fill rx ring with buffers */
+       priv->rx_buf = kzalloc(sizeof(unsigned char *) * priv->rx_ring_size,
                               GFP_KERNEL);
-       if (!priv->rx_skb) {
-               dev_err(kdev, "cannot allocate rx skb queue\n");
+       if (!priv->rx_buf) {
+               dev_err(kdev, "cannot allocate rx buffer queue\n");
                ret = -ENOMEM;
                goto out_free_tx_skb;
        }
@@ -707,8 +742,8 @@ static int bcm6368_enetsw_open(struct net_device *dev)
        dma_writel(priv, DMA_BUFALLOC_FORCE_MASK | 0,
                   DMA_BUFALLOC_REG(priv->rx_chan));
 
-       if (bcm6368_enetsw_refill_rx(dev)) {
-               dev_err(kdev, "cannot allocate rx skb queue\n");
+       if (bcm6368_enetsw_refill_rx(dev, false)) {
+               dev_err(kdev, "cannot allocate rx buffer queue\n");
                ret = -ENOMEM;
                goto out;
        }
@@ -770,15 +805,15 @@ out:
        for (i = 0; i < priv->rx_ring_size; i++) {
                struct bcm6368_enetsw_desc *desc;
 
-               if (!priv->rx_skb[i])
+               if (!priv->rx_buf[i])
                        continue;
 
                desc = &priv->rx_desc_cpu[i];
-               dma_unmap_single(kdev, desc->address, priv->rx_skb_size,
+               dma_unmap_single(kdev, desc->address, priv->rx_buf_size,
                                 DMA_FROM_DEVICE);
-               kfree_skb(priv->rx_skb[i]);
+               skb_free_frag(priv->rx_buf[i]);
        }
-       kfree(priv->rx_skb);
+       kfree(priv->rx_buf);
 
 out_free_tx_skb:
        kfree(priv->tx_skb);
@@ -821,24 +856,24 @@ static int bcm6368_enetsw_stop(struct net_device *dev)
        bcm6368_enetsw_disable_dma(priv, priv->rx_chan);
 
        /* force reclaim of all tx buffers */
-       bcm6368_enetsw_tx_reclaim(dev, 1);
+       bcm6368_enetsw_tx_reclaim(dev, 1, 0);
 
-       /* free the rx skb ring */
+       /* free the rx buffer ring */
        for (i = 0; i < priv->rx_ring_size; i++) {
                struct bcm6368_enetsw_desc *desc;
 
-               if (!priv->rx_skb[i])
+               if (!priv->rx_buf[i])
                        continue;
 
                desc = &priv->rx_desc_cpu[i];
-               dma_unmap_single_attrs(kdev, desc->address, priv->rx_skb_size,
+               dma_unmap_single_attrs(kdev, desc->address, priv->rx_buf_size,
                                       DMA_FROM_DEVICE,
                                       DMA_ATTR_SKIP_CPU_SYNC);
-               kfree_skb(priv->rx_skb[i]);
+               skb_free_frag(priv->rx_buf[i]);
        }
 
        /* free remaining allocated memory */
-       kfree(priv->rx_skb);
+       kfree(priv->rx_buf);
        kfree(priv->tx_skb);
        dma_free_coherent(kdev, priv->rx_desc_alloc_size,
                          priv->rx_desc_cpu, priv->rx_desc_dma);
@@ -848,6 +883,8 @@ static int bcm6368_enetsw_stop(struct net_device *dev)
                free_irq(priv->irq_tx, dev);
        free_irq(priv->irq_rx, dev);
 
+       netdev_reset_queue(dev);
+
        return 0;
 }
 
@@ -960,9 +997,11 @@ static int bcm6368_enetsw_probe(struct platform_device *pdev)
                dev_info(dev, "random mac %pM\n", ndev->dev_addr);
        }
 
-       priv->rx_skb_size = ALIGN(ndev->mtu + ENETSW_MTU_OVERHEAD,
+       priv->rx_buf_size = ALIGN(ndev->mtu + ENETSW_MTU_OVERHEAD,
                                  priv->dma_maxburst * 4);
 
+       priv->rx_frag_size = ENETSW_FRAG_SIZE(priv->rx_buf_size);
+
        priv->num_clocks = of_clk_get_parent_count(node);
        if (priv->num_clocks) {
                priv->clock = devm_kcalloc(dev, priv->num_clocks,