Merge master.kernel.org:/pub/scm/linux/kernel/git/lethal/sh-2.6.23
[sfrench/cifs-2.6.git] / drivers / net / myri10ge / myri10ge.c
index 5d14be7405a37e29c6a75ee73d9409f25f0d9aa3..ae9bb7b7fd6756d240c7b2587a8d6afd5d23c5ec 100644 (file)
@@ -60,6 +60,7 @@
 #include <linux/crc32.h>
 #include <linux/moduleparam.h>
 #include <linux/io.h>
+#include <linux/log2.h>
 #include <net/checksum.h>
 #include <asm/byteorder.h>
 #include <asm/io.h>
@@ -71,7 +72,7 @@
 #include "myri10ge_mcp.h"
 #include "myri10ge_mcp_gen_header.h"
 
-#define MYRI10GE_VERSION_STR "1.3.0-1.233"
+#define MYRI10GE_VERSION_STR "1.3.1-1.248"
 
 MODULE_DESCRIPTION("Myricom 10G driver (10GbE)");
 MODULE_AUTHOR("Maintainer: help@myri.com");
@@ -190,6 +191,7 @@ struct myri10ge_priv {
        struct timer_list watchdog_timer;
        int watchdog_tx_done;
        int watchdog_tx_req;
+       int watchdog_pause;
        int watchdog_resets;
        int tx_linearized;
        int pause;
@@ -279,6 +281,8 @@ static int myri10ge_fill_thresh = 256;
 module_param(myri10ge_fill_thresh, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed\n");
 
+static int myri10ge_reset_recover = 1;
+
 static int myri10ge_wcfifo = 0;
 module_param(myri10ge_wcfifo, int, S_IRUGO);
 MODULE_PARM_DESC(myri10ge_wcfifo, "Enable WC Fifo when WC is enabled\n");
@@ -1057,7 +1061,6 @@ static inline void myri10ge_tx_done(struct myri10ge_priv *mgp, int mcp_index)
        struct myri10ge_tx_buf *tx = &mgp->tx;
        struct sk_buff *skb;
        int idx, len;
-       int limit = 0;
 
        while (tx->pkt_done != mcp_index) {
                idx = tx->done & tx->mask;
@@ -1088,11 +1091,6 @@ static inline void myri10ge_tx_done(struct myri10ge_priv *mgp, int mcp_index)
                                                              bus), len,
                                               PCI_DMA_TODEVICE);
                }
-
-               /* limit potential for livelock by only handling
-                * 2 full tx rings per call */
-               if (unlikely(++limit > 2 * tx->mask))
-                       break;
        }
        /* start the queue if we've stopped it */
        if (netif_queue_stopped(mgp->dev)
@@ -1154,9 +1152,11 @@ static inline void myri10ge_check_statblock(struct myri10ge_priv *mgp)
        struct mcp_irq_data *stats = mgp->fw_stats;
 
        if (unlikely(stats->stats_updated)) {
-               if (mgp->link_state != stats->link_up) {
-                       mgp->link_state = stats->link_up;
-                       if (mgp->link_state) {
+               unsigned link_up = ntohl(stats->link_up);
+               if (mgp->link_state != link_up) {
+                       mgp->link_state = link_up;
+
+                       if (mgp->link_state == MXGEFW_LINK_UP) {
                                if (netif_msg_link(mgp))
                                        printk(KERN_INFO
                                               "myri10ge: %s: link up\n",
@@ -1166,8 +1166,11 @@ static inline void myri10ge_check_statblock(struct myri10ge_priv *mgp)
                        } else {
                                if (netif_msg_link(mgp))
                                        printk(KERN_INFO
-                                              "myri10ge: %s: link down\n",
-                                              mgp->dev->name);
+                                              "myri10ge: %s: link %s\n",
+                                              mgp->dev->name,
+                                              (link_up == MXGEFW_LINK_MYRINET ?
+                                               "mismatch (Myrinet detected)" :
+                                               "down"));
                                netif_carrier_off(mgp->dev);
                                mgp->link_changes++;
                        }
@@ -1472,6 +1475,7 @@ static const struct ethtool_ops myri10ge_ethtool_ops = {
        .set_sg = ethtool_op_set_sg,
        .get_tso = ethtool_op_get_tso,
        .set_tso = ethtool_op_set_tso,
+       .get_link = ethtool_op_get_link,
        .get_strings = myri10ge_get_strings,
        .get_stats_count = myri10ge_get_stats_count,
        .get_ethtool_stats = myri10ge_get_ethtool_stats,
@@ -1796,7 +1800,7 @@ static int myri10ge_open(struct net_device *dev)
         */
        big_pow2 = dev->mtu + ETH_HLEN + VLAN_HLEN + MXGEFW_PAD;
        if (big_pow2 < MYRI10GE_ALLOC_SIZE / 2) {
-               while ((big_pow2 & (big_pow2 - 1)) != 0)
+               while (!is_power_of_2(big_pow2))
                        big_pow2++;
                mgp->big_bytes = dev->mtu + ETH_HLEN + VLAN_HLEN + MXGEFW_PAD;
        } else {
@@ -2729,8 +2733,14 @@ static void myri10ge_watchdog(struct work_struct *work)
                 * For now, just report it */
                reboot = myri10ge_read_reboot(mgp);
                printk(KERN_ERR
-                      "myri10ge: %s: NIC rebooted (0x%x), resetting\n",
-                      mgp->dev->name, reboot);
+                      "myri10ge: %s: NIC rebooted (0x%x),%s resetting\n",
+                      mgp->dev->name, reboot,
+                      myri10ge_reset_recover ? " " : " not");
+               if (myri10ge_reset_recover == 0)
+                       return;
+
+               myri10ge_reset_recover--;
+
                /*
                 * A rebooted nic will come back with config space as
                 * it was after power was applied to PCIe bus.
@@ -2791,6 +2801,7 @@ static void myri10ge_watchdog(struct work_struct *work)
 static void myri10ge_watchdog_timer(unsigned long arg)
 {
        struct myri10ge_priv *mgp;
+       u32 rx_pause_cnt;
 
        mgp = (struct myri10ge_priv *)arg;
 
@@ -2807,19 +2818,28 @@ static void myri10ge_watchdog_timer(unsigned long arg)
                    myri10ge_fill_thresh)
                        mgp->rx_big.watchdog_needed = 0;
        }
+       rx_pause_cnt = ntohl(mgp->fw_stats->dropped_pause);
 
        if (mgp->tx.req != mgp->tx.done &&
            mgp->tx.done == mgp->watchdog_tx_done &&
-           mgp->watchdog_tx_req != mgp->watchdog_tx_done)
+           mgp->watchdog_tx_req != mgp->watchdog_tx_done) {
                /* nic seems like it might be stuck.. */
-               schedule_work(&mgp->watchdog_work);
-       else
-               /* rearm timer */
-               mod_timer(&mgp->watchdog_timer,
-                         jiffies + myri10ge_watchdog_timeout * HZ);
-
+               if (rx_pause_cnt != mgp->watchdog_pause) {
+                       if (net_ratelimit())
+                               printk(KERN_WARNING "myri10ge %s:"
+                                      "TX paused, check link partner\n",
+                                      mgp->dev->name);
+               } else {
+                       schedule_work(&mgp->watchdog_work);
+                       return;
+               }
+       }
+       /* rearm timer */
+       mod_timer(&mgp->watchdog_timer,
+                 jiffies + myri10ge_watchdog_timeout * HZ);
        mgp->watchdog_tx_done = mgp->tx.done;
        mgp->watchdog_tx_req = mgp->tx.req;
+       mgp->watchdog_pause = rx_pause_cnt;
 }
 
 static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -2840,6 +2860,8 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                return -ENOMEM;
        }
 
+       SET_NETDEV_DEV(netdev, &pdev->dev);
+
        mgp = netdev_priv(netdev);
        memset(mgp, 0, sizeof(*mgp));
        mgp->dev = netdev;