Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / drivers / net / mv643xx_eth.c
index 3bbbd94..a56d9d2 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/mv643xx_eth.h>
 #include <linux/io.h>
 #include <linux/types.h>
+#include <linux/inet_lro.h>
 #include <asm/system.h>
 
 static char mv643xx_eth_driver_name[] = "mv643xx_eth";
@@ -227,6 +228,12 @@ struct tx_desc {
 #define RX_ENABLE_INTERRUPT            0x20000000
 #define RX_FIRST_DESC                  0x08000000
 #define RX_LAST_DESC                   0x04000000
+#define RX_IP_HDR_OK                   0x02000000
+#define RX_PKT_IS_IPV4                 0x01000000
+#define RX_PKT_IS_ETHERNETV2           0x00800000
+#define RX_PKT_LAYER4_TYPE_MASK                0x00600000
+#define RX_PKT_LAYER4_TYPE_TCP_IPV4    0x00000000
+#define RX_PKT_IS_VLAN_TAGGED          0x00080000
 
 /* TX descriptor command */
 #define TX_ENABLE_INTERRUPT            0x00800000
@@ -324,6 +331,12 @@ struct mib_counters {
        u32 late_collision;
 };
 
+struct lro_counters {
+       u32 lro_aggregated;
+       u32 lro_flushed;
+       u32 lro_no_desc;
+};
+
 struct rx_queue {
        int index;
 
@@ -337,6 +350,9 @@ struct rx_queue {
        dma_addr_t rx_desc_dma;
        int rx_desc_area_size;
        struct sk_buff **rx_skb;
+
+       struct net_lro_mgr lro_mgr;
+       struct net_lro_desc lro_arr[8];
 };
 
 struct tx_queue {
@@ -372,6 +388,8 @@ struct mv643xx_eth_private {
        spinlock_t mib_counters_lock;
        struct mib_counters mib_counters;
 
+       struct lro_counters lro_counters;
+
        struct work_struct tx_timeout_task;
 
        struct napi_struct napi;
@@ -496,12 +514,40 @@ static void txq_maybe_wake(struct tx_queue *txq)
 
 
 /* rx napi ******************************************************************/
+static int
+mv643xx_get_skb_header(struct sk_buff *skb, void **iphdr, void **tcph,
+                      u64 *hdr_flags, void *priv)
+{
+       unsigned long cmd_sts = (unsigned long)priv;
+
+       /*
+        * Make sure that this packet is Ethernet II, is not VLAN
+        * tagged, is IPv4, has a valid IP header, and is TCP.
+        */
+       if ((cmd_sts & (RX_IP_HDR_OK | RX_PKT_IS_IPV4 |
+                      RX_PKT_IS_ETHERNETV2 | RX_PKT_LAYER4_TYPE_MASK |
+                      RX_PKT_IS_VLAN_TAGGED)) !=
+           (RX_IP_HDR_OK | RX_PKT_IS_IPV4 |
+            RX_PKT_IS_ETHERNETV2 | RX_PKT_LAYER4_TYPE_TCP_IPV4))
+               return -1;
+
+       skb_reset_network_header(skb);
+       skb_set_transport_header(skb, ip_hdrlen(skb));
+       *iphdr = ip_hdr(skb);
+       *tcph = tcp_hdr(skb);
+       *hdr_flags = LRO_IPV4 | LRO_TCP;
+
+       return 0;
+}
+
 static int rxq_process(struct rx_queue *rxq, int budget)
 {
        struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
        struct net_device_stats *stats = &mp->dev->stats;
+       int lro_flush_needed;
        int rx;
 
+       lro_flush_needed = 0;
        rx = 0;
        while (rx < budget && rxq->rx_desc_count) {
                struct rx_desc *rx_desc;
@@ -561,7 +607,13 @@ static int rxq_process(struct rx_queue *rxq, int budget)
                if (cmd_sts & LAYER_4_CHECKSUM_OK)
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                skb->protocol = eth_type_trans(skb, mp->dev);
-               netif_receive_skb(skb);
+
+               if (skb->dev->features & NETIF_F_LRO &&
+                   skb->ip_summed == CHECKSUM_UNNECESSARY) {
+                       lro_receive_skb(&rxq->lro_mgr, skb, (void *)cmd_sts);
+                       lro_flush_needed = 1;
+               } else
+                       netif_receive_skb(skb);
 
                continue;
 
@@ -582,6 +634,9 @@ err:
                dev_kfree_skb(skb);
        }
 
+       if (lro_flush_needed)
+               lro_flush_all(&rxq->lro_mgr);
+
        if (rx < budget)
                mp->work_rx &= ~(1 << rxq->index);
 
@@ -1161,6 +1216,26 @@ static struct net_device_stats *mv643xx_eth_get_stats(struct net_device *dev)
        return stats;
 }
 
+static void mv643xx_eth_grab_lro_stats(struct mv643xx_eth_private *mp)
+{
+       u32 lro_aggregated = 0;
+       u32 lro_flushed = 0;
+       u32 lro_no_desc = 0;
+       int i;
+
+       for (i = 0; i < mp->rxq_count; i++) {
+               struct rx_queue *rxq = mp->rxq + i;
+
+               lro_aggregated += rxq->lro_mgr.stats.aggregated;
+               lro_flushed += rxq->lro_mgr.stats.flushed;
+               lro_no_desc += rxq->lro_mgr.stats.no_desc;
+       }
+
+       mp->lro_counters.lro_aggregated = lro_aggregated;
+       mp->lro_counters.lro_flushed = lro_flushed;
+       mp->lro_counters.lro_no_desc = lro_no_desc;
+}
+
 static inline u32 mib_read(struct mv643xx_eth_private *mp, int offset)
 {
        return rdl(mp, MIB_COUNTERS(mp->port_num) + offset);
@@ -1178,7 +1253,7 @@ static void mib_counters_update(struct mv643xx_eth_private *mp)
 {
        struct mib_counters *p = &mp->mib_counters;
 
-       spin_lock(&mp->mib_counters_lock);
+       spin_lock_bh(&mp->mib_counters_lock);
        p->good_octets_received += mib_read(mp, 0x00);
        p->good_octets_received += (u64)mib_read(mp, 0x04) << 32;
        p->bad_octets_received += mib_read(mp, 0x08);
@@ -1211,7 +1286,7 @@ static void mib_counters_update(struct mv643xx_eth_private *mp)
        p->bad_crc_event += mib_read(mp, 0x74);
        p->collision += mib_read(mp, 0x78);
        p->late_collision += mib_read(mp, 0x7c);
-       spin_unlock(&mp->mib_counters_lock);
+       spin_unlock_bh(&mp->mib_counters_lock);
 
        mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ);
 }
@@ -1319,6 +1394,10 @@ struct mv643xx_eth_stats {
        { #m, FIELD_SIZEOF(struct mib_counters, m),             \
          -1, offsetof(struct mv643xx_eth_private, mib_counters.m) }
 
+#define LROSTAT(m)                                             \
+       { #m, FIELD_SIZEOF(struct lro_counters, m),             \
+         -1, offsetof(struct mv643xx_eth_private, lro_counters.m) }
+
 static const struct mv643xx_eth_stats mv643xx_eth_stats[] = {
        SSTAT(rx_packets),
        SSTAT(tx_packets),
@@ -1358,6 +1437,9 @@ static const struct mv643xx_eth_stats mv643xx_eth_stats[] = {
        MIBSTAT(bad_crc_event),
        MIBSTAT(collision),
        MIBSTAT(late_collision),
+       LROSTAT(lro_aggregated),
+       LROSTAT(lro_flushed),
+       LROSTAT(lro_no_desc),
 };
 
 static int
@@ -1569,6 +1651,7 @@ static void mv643xx_eth_get_ethtool_stats(struct net_device *dev,
 
        mv643xx_eth_get_stats(dev);
        mib_counters_update(mp);
+       mv643xx_eth_grab_lro_stats(mp);
 
        for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) {
                const struct mv643xx_eth_stats *stat;
@@ -1606,9 +1689,12 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops = {
        .set_ringparam          = mv643xx_eth_set_ringparam,
        .get_rx_csum            = mv643xx_eth_get_rx_csum,
        .set_rx_csum            = mv643xx_eth_set_rx_csum,
+       .set_tx_csum            = ethtool_op_set_tx_csum,
        .set_sg                 = ethtool_op_set_sg,
        .get_strings            = mv643xx_eth_get_strings,
        .get_ethtool_stats      = mv643xx_eth_get_ethtool_stats,
+       .get_flags              = ethtool_op_get_flags,
+       .set_flags              = ethtool_op_set_flags,
        .get_sset_count         = mv643xx_eth_get_sset_count,
 };
 
@@ -1735,7 +1821,7 @@ oom:
                return;
        }
 
-       mc_spec = kmalloc(0x200, GFP_KERNEL);
+       mc_spec = kmalloc(0x200, GFP_ATOMIC);
        if (mc_spec == NULL)
                goto oom;
        mc_other = mc_spec + (0x100 >> 2);
@@ -1843,6 +1929,19 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index)
                                        nexti * sizeof(struct rx_desc);
        }
 
+       rxq->lro_mgr.dev = mp->dev;
+       memset(&rxq->lro_mgr.stats, 0, sizeof(rxq->lro_mgr.stats));
+       rxq->lro_mgr.features = LRO_F_NAPI;
+       rxq->lro_mgr.ip_summed = CHECKSUM_UNNECESSARY;
+       rxq->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY;
+       rxq->lro_mgr.max_desc = ARRAY_SIZE(rxq->lro_arr);
+       rxq->lro_mgr.max_aggr = 32;
+       rxq->lro_mgr.frag_align_pad = 0;
+       rxq->lro_mgr.lro_arr = rxq->lro_arr;
+       rxq->lro_mgr.get_skb_header = mv643xx_get_skb_header;
+
+       memset(&rxq->lro_arr, 0, sizeof(rxq->lro_arr));
+
        return 0;
 
 
@@ -2190,11 +2289,6 @@ static void port_start(struct mv643xx_eth_private *mp)
        }
 
        /*
-        * Add configured unicast address to address filter table.
-        */
-       mv643xx_eth_program_unicast_filter(mp->dev);
-
-       /*
         * Receive all unmatched unicast, TCP, UDP, BPDU and broadcast
         * frames to RX queue #0, and include the pseudo-header when
         * calculating receive checksums.
@@ -2207,6 +2301,11 @@ static void port_start(struct mv643xx_eth_private *mp)
        wrlp(mp, PORT_CONFIG_EXT, 0x00000000);
 
        /*
+        * Add configured unicast addresses to address filter table.
+        */
+       mv643xx_eth_program_unicast_filter(mp->dev);
+
+       /*
         * Enable the receive queues.
         */
        for (i = 0; i < mp->rxq_count; i++) {
@@ -2289,13 +2388,8 @@ static int mv643xx_eth_open(struct net_device *dev)
                }
        }
 
-       netif_carrier_off(dev);
-
        port_start(mp);
 
-       set_rx_coal(mp, 0);
-       set_tx_coal(mp, 0);
-
        wrlp(mp, INT_MASK_EXT, INT_EXT_LINK_PHY | INT_EXT_TX);
        wrlp(mp, INT_MASK, INT_TX_END | INT_RX | INT_EXT);
 
@@ -2346,8 +2440,6 @@ static int mv643xx_eth_stop(struct net_device *dev)
        wrlp(mp, INT_MASK, 0x00000000);
        rdlp(mp, INT_MASK);
 
-       del_timer_sync(&mp->mib_counters_timer);
-
        napi_disable(&mp->napi);
 
        del_timer_sync(&mp->rx_oom);
@@ -2359,6 +2451,7 @@ static int mv643xx_eth_stop(struct net_device *dev)
        port_reset(mp);
        mv643xx_eth_get_stats(dev);
        mib_counters_update(mp);
+       del_timer_sync(&mp->mib_counters_timer);
 
        skb_queue_purge(&mp->rx_recycle);
 
@@ -2763,6 +2856,21 @@ static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex)
        wrlp(mp, PORT_SERIAL_CONTROL, pscr);
 }
 
+static const struct net_device_ops mv643xx_eth_netdev_ops = {
+       .ndo_open               = mv643xx_eth_open,
+       .ndo_stop               = mv643xx_eth_stop,
+       .ndo_start_xmit         = mv643xx_eth_xmit,
+       .ndo_set_rx_mode        = mv643xx_eth_set_rx_mode,
+       .ndo_set_mac_address    = mv643xx_eth_set_mac_address,
+       .ndo_do_ioctl           = mv643xx_eth_ioctl,
+       .ndo_change_mtu         = mv643xx_eth_change_mtu,
+       .ndo_tx_timeout         = mv643xx_eth_tx_timeout,
+       .ndo_get_stats          = mv643xx_eth_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = mv643xx_eth_netpoll,
+#endif
+};
+
 static int mv643xx_eth_probe(struct platform_device *pdev)
 {
        struct mv643xx_eth_platform_data *pd;
@@ -2834,18 +2942,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        BUG_ON(!res);
        dev->irq = res->start;
 
-       dev->get_stats = mv643xx_eth_get_stats;
-       dev->hard_start_xmit = mv643xx_eth_xmit;
-       dev->open = mv643xx_eth_open;
-       dev->stop = mv643xx_eth_stop;
-       dev->set_rx_mode = mv643xx_eth_set_rx_mode;
-       dev->set_mac_address = mv643xx_eth_set_mac_address;
-       dev->do_ioctl = mv643xx_eth_ioctl;
-       dev->change_mtu = mv643xx_eth_change_mtu;
-       dev->tx_timeout = mv643xx_eth_tx_timeout;
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       dev->poll_controller = mv643xx_eth_netpoll;
-#endif
+       dev->netdev_ops = &mv643xx_eth_netdev_ops;
+
        dev->watchdog_timeo = 2 * HZ;
        dev->base_addr = 0;
 
@@ -2857,6 +2955,11 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        if (mp->shared->win_protect)
                wrl(mp, WINDOW_PROTECT(mp->port_num), mp->shared->win_protect);
 
+       netif_carrier_off(dev);
+
+       set_rx_coal(mp, 250);
+       set_tx_coal(mp, 0);
+
        err = register_netdev(dev);
        if (err)
                goto out;