]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/mv643xx_eth.c
Merge branch 'proc' of git://git.kernel.org/pub/scm/linux/kernel/git/adobriyan/proc
[linux-2.6-omap-h63xx.git] / drivers / net / mv643xx_eth.c
index 55aa8ba7e0f25eb3fd8101d3732c222e3967bf39..a9c8c08044b1847ebe560964433ad3ee4a564322 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/init.h>
 #include <linux/dma-mapping.h>
 #include <linux/in.h>
+#include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/etherdevice.h>
@@ -250,7 +251,7 @@ struct mv643xx_eth_shared_private {
        /*
         * Provides access to local SMI interface.
         */
-       struct mii_bus smi_bus;
+       struct mii_bus *smi_bus;
 
        /*
         * If we have access to the error interrupt pin (which is
@@ -370,6 +371,9 @@ struct mv643xx_eth_private {
        u8 work_rx_refill;
        u8 work_rx_oom;
 
+       int skb_size;
+       struct sk_buff_head rx_recycle;
+
        /*
         * RX state.
         */
@@ -566,31 +570,19 @@ static int rxq_process(struct rx_queue *rxq, int budget)
 static int rxq_refill(struct rx_queue *rxq, int budget)
 {
        struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
-       int skb_size;
        int refilled;
 
-       /*
-        * Reserve 2+14 bytes for an ethernet header (the hardware
-        * automatically prepends 2 bytes of dummy data to each
-        * received packet), 16 bytes for up to four VLAN tags, and
-        * 4 bytes for the trailing FCS -- 36 bytes total.
-        */
-       skb_size = rxq_to_mp(rxq)->dev->mtu + 36;
-
-       /*
-        * Make sure that the skb size is a multiple of 8 bytes, as
-        * the lower three bits of the receive descriptor's buffer
-        * size field are ignored by the hardware.
-        */
-       skb_size = (skb_size + 7) & ~7;
-
        refilled = 0;
        while (refilled < budget && rxq->rx_desc_count < rxq->rx_ring_size) {
                struct sk_buff *skb;
                int unaligned;
                int rx;
 
-               skb = dev_alloc_skb(skb_size + dma_get_cache_alignment() - 1);
+               skb = __skb_dequeue(&mp->rx_recycle);
+               if (skb == NULL)
+                       skb = dev_alloc_skb(mp->skb_size +
+                                           dma_get_cache_alignment() - 1);
+
                if (skb == NULL) {
                        mp->work_rx_oom |= 1 << rxq->index;
                        goto oom;
@@ -608,8 +600,8 @@ static int rxq_refill(struct rx_queue *rxq, int budget)
                        rxq->rx_used_desc = 0;
 
                rxq->rx_desc_area[rx].buf_ptr = dma_map_single(NULL, skb->data,
-                                               skb_size, DMA_FROM_DEVICE);
-               rxq->rx_desc_area[rx].buf_size = skb_size;
+                                               mp->skb_size, DMA_FROM_DEVICE);
+               rxq->rx_desc_area[rx].buf_size = mp->skb_size;
                rxq->rx_skb[rx] = skb;
                wmb();
                rxq->rx_desc_area[rx].cmd_sts = BUFFER_OWNED_BY_DMA |
@@ -904,8 +896,14 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force)
                                       desc->byte_cnt, DMA_TO_DEVICE);
                }
 
-               if (skb)
-                       dev_kfree_skb(skb);
+               if (skb != NULL) {
+                       if (skb_queue_len(&mp->rx_recycle) <
+                                       mp->default_rx_ring_size &&
+                           skb_recycle_check(skb, mp->skb_size))
+                               __skb_queue_head(&mp->rx_recycle, skb);
+                       else
+                               dev_kfree_skb(skb);
+               }
        }
 
        __netif_tx_unlock(nq);
@@ -2042,6 +2040,26 @@ static void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int delay)
        wrl(mp, TX_FIFO_URGENT_THRESHOLD(mp->port_num), (coal & 0x3fff) << 4);
 }
 
+static void mv643xx_eth_recalc_skb_size(struct mv643xx_eth_private *mp)
+{
+       int skb_size;
+
+       /*
+        * Reserve 2+14 bytes for an ethernet header (the hardware
+        * automatically prepends 2 bytes of dummy data to each
+        * received packet), 16 bytes for up to four VLAN tags, and
+        * 4 bytes for the trailing FCS -- 36 bytes total.
+        */
+       skb_size = mp->dev->mtu + 36;
+
+       /*
+        * Make sure that the skb size is a multiple of 8 bytes, as
+        * the lower three bits of the receive descriptor's buffer
+        * size field are ignored by the hardware.
+        */
+       mp->skb_size = (skb_size + 7) & ~7;
+}
+
 static int mv643xx_eth_open(struct net_device *dev)
 {
        struct mv643xx_eth_private *mp = netdev_priv(dev);
@@ -2061,8 +2079,12 @@ static int mv643xx_eth_open(struct net_device *dev)
 
        init_mac_tables(mp);
 
+       mv643xx_eth_recalc_skb_size(mp);
+
        napi_enable(&mp->napi);
 
+       skb_queue_head_init(&mp->rx_recycle);
+
        for (i = 0; i < mp->rxq_count; i++) {
                err = rxq_init(mp, i);
                if (err) {
@@ -2158,6 +2180,8 @@ static int mv643xx_eth_stop(struct net_device *dev)
        mv643xx_eth_get_stats(dev);
        mib_counters_update(mp);
 
+       skb_queue_purge(&mp->rx_recycle);
+
        for (i = 0; i < mp->rxq_count; i++)
                rxq_deinit(mp->rxq + i);
        for (i = 0; i < mp->txq_count; i++)
@@ -2184,6 +2208,7 @@ static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu)
                return -EINVAL;
 
        dev->mtu = new_mtu;
+       mv643xx_eth_recalc_skb_size(mp);
        tx_set_rate(mp, 1000000000, 16777216);
 
        if (!netif_running(dev))
@@ -2339,15 +2364,19 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
         * Set up and register SMI bus.
         */
        if (pd == NULL || pd->shared_smi == NULL) {
-               msp->smi_bus.priv = msp;
-               msp->smi_bus.name = "mv643xx_eth smi";
-               msp->smi_bus.read = smi_bus_read;
-               msp->smi_bus.write = smi_bus_write,
-               snprintf(msp->smi_bus.id, MII_BUS_ID_SIZE, "%d", pdev->id);
-               msp->smi_bus.dev = &pdev->dev;
-               msp->smi_bus.phy_mask = 0xffffffff;
-               if (mdiobus_register(&msp->smi_bus) < 0)
+               msp->smi_bus = mdiobus_alloc();
+               if (msp->smi_bus == NULL)
                        goto out_unmap;
+
+               msp->smi_bus->priv = msp;
+               msp->smi_bus->name = "mv643xx_eth smi";
+               msp->smi_bus->read = smi_bus_read;
+               msp->smi_bus->write = smi_bus_write,
+               snprintf(msp->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
+               msp->smi_bus->parent = &pdev->dev;
+               msp->smi_bus->phy_mask = 0xffffffff;
+               if (mdiobus_register(msp->smi_bus) < 0)
+                       goto out_free_mii_bus;
                msp->smi = msp;
        } else {
                msp->smi = platform_get_drvdata(pd->shared_smi);
@@ -2387,6 +2416,8 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
 
        return 0;
 
+out_free_mii_bus:
+       mdiobus_free(msp->smi_bus);
 out_unmap:
        iounmap(msp->base);
 out_free:
@@ -2400,8 +2431,10 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev)
        struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev);
        struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data;
 
-       if (pd == NULL || pd->shared_smi == NULL)
-               mdiobus_unregister(&msp->smi_bus);
+       if (pd == NULL || pd->shared_smi == NULL) {
+               mdiobus_free(msp->smi_bus);
+               mdiobus_unregister(msp->smi_bus);
+       }
        if (msp->err_interrupt != NO_IRQ)
                free_irq(msp->err_interrupt, msp);
        iounmap(msp->base);
@@ -2469,7 +2502,7 @@ static void set_params(struct mv643xx_eth_private *mp,
 static struct phy_device *phy_scan(struct mv643xx_eth_private *mp,
                                   int phy_addr)
 {
-       struct mii_bus *bus = &mp->shared->smi->smi_bus;
+       struct mii_bus *bus = mp->shared->smi->smi_bus;
        struct phy_device *phydev;
        int start;
        int num;