ixgbe: Update watchdog thread to accomodate longerlink_up events
[safe/jmp/linux-2.6] / drivers / net / ixgbe / ixgbe_main.c
index 9048195..036393e 100644 (file)
@@ -902,6 +902,20 @@ static void ixgbe_set_itr_msix(struct ixgbe_q_vector *q_vector)
        return;
 }
 
+
+static void ixgbe_check_lsc(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       adapter->lsc_int++;
+       adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE;
+       adapter->link_check_timeout = jiffies;
+       if (!test_bit(__IXGBE_DOWN, &adapter->state)) {
+               IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_LSC);
+               schedule_work(&adapter->watchdog_task);
+       }
+}
+
 static irqreturn_t ixgbe_msix_lsc(int irq, void *data)
 {
        struct net_device *netdev = data;
@@ -909,11 +923,8 @@ static irqreturn_t ixgbe_msix_lsc(int irq, void *data)
        struct ixgbe_hw *hw = &adapter->hw;
        u32 eicr = IXGBE_READ_REG(hw, IXGBE_EICR);
 
-       if (eicr & IXGBE_EICR_LSC) {
-               adapter->lsc_int++;
-               if (!test_bit(__IXGBE_DOWN, &adapter->state))
-                       mod_timer(&adapter->watchdog_timer, jiffies);
-       }
+       if (eicr & IXGBE_EICR_LSC)
+               ixgbe_check_lsc(adapter);
 
        if (!test_bit(__IXGBE_DOWN, &adapter->state))
                IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_OTHER);
@@ -1237,12 +1248,8 @@ static irqreturn_t ixgbe_intr(int irq, void *data)
        if (!eicr)
                return IRQ_NONE;        /* Not our interrupt */
 
-       if (eicr & IXGBE_EICR_LSC) {
-               adapter->lsc_int++;
-               if (!test_bit(__IXGBE_DOWN, &adapter->state))
-                       mod_timer(&adapter->watchdog_timer, jiffies);
-       }
-
+       if (eicr & IXGBE_EICR_LSC)
+               ixgbe_check_lsc(adapter);
 
        if (netif_rx_schedule_prep(netdev, &adapter->q_vector[0].napi)) {
                adapter->tx_ring[0].total_packets = 0;
@@ -1897,6 +1904,8 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter)
 
        /* bring the link up in the watchdog, this could race with our first
         * link up interrupt but shouldn't be a problem */
+       adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE;
+       adapter->link_check_timeout = jiffies;
        mod_timer(&adapter->watchdog_timer, jiffies);
        return 0;
 }
@@ -2098,6 +2107,7 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
 
        ixgbe_napi_disable_all(adapter);
        del_timer_sync(&adapter->watchdog_timer);
+       cancel_work_sync(&adapter->watchdog_task);
 
        netif_carrier_off(netdev);
        netif_tx_stop_all_queues(netdev);
@@ -3010,27 +3020,74 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter)
 static void ixgbe_watchdog(unsigned long data)
 {
        struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data;
-       struct net_device *netdev = adapter->netdev;
-       bool link_up;
-       u32 link_speed = 0;
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       /* Do the watchdog outside of interrupt context due to the lovely
+        * delays that some of the newer hardware requires */
+       if (!test_bit(__IXGBE_DOWN, &adapter->state)) {
+               /* Cause software interrupt to ensure rx rings are cleaned */
+               if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
+                       u32 eics =
+                        (1 << (adapter->num_msix_vectors - NON_Q_VECTORS)) - 1;
+                       IXGBE_WRITE_REG(hw, IXGBE_EICS, eics);
+               } else {
+                       /* For legacy and MSI interrupts don't set any bits that
+                        * are enabled for EIAM, because this operation would
+                        * set *both* EIMS and EICS for any bit in EIAM */
+                       IXGBE_WRITE_REG(hw, IXGBE_EICS,
+                                    (IXGBE_EICS_TCP_TIMER | IXGBE_EICS_OTHER));
+               }
+               /* Reset the timer */
+               mod_timer(&adapter->watchdog_timer,
+                         round_jiffies(jiffies + 2 * HZ));
+       }
 
-       adapter->hw.mac.ops.check_link(&adapter->hw, &(link_speed), &link_up);
+       schedule_work(&adapter->watchdog_task);
+}
+
+/**
+ *  ixgbe_watchdog_task - worker thread to bring link up
+ *  @work: pointer to work_struct containing our data
+ **/
+static void ixgbe_watchdog_task(struct work_struct *work)
+{
+       struct ixgbe_adapter *adapter = container_of(work,
+                                                    struct ixgbe_adapter,
+                                                    watchdog_task);
+       struct net_device *netdev = adapter->netdev;
+       struct ixgbe_hw *hw = &adapter->hw;
+       u32 link_speed = adapter->link_speed;
+       bool link_up = adapter->link_up;
+
+       adapter->flags |= IXGBE_FLAG_IN_WATCHDOG_TASK;
+
+       if (adapter->flags & IXGBE_FLAG_NEED_LINK_UPDATE) {
+               hw->mac.ops.check_link(hw, &link_speed, &link_up, false);
+               if (link_up ||
+                   time_after(jiffies, (adapter->link_check_timeout +
+                                        IXGBE_TRY_LINK_TIMEOUT))) {
+                       IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMC_LSC);
+                       adapter->flags &= ~IXGBE_FLAG_NEED_LINK_UPDATE;
+               }
+               adapter->link_up = link_up;
+               adapter->link_speed = link_speed;
+       }
 
        if (link_up) {
                if (!netif_carrier_ok(netdev)) {
-                       u32 frctl = IXGBE_READ_REG(&adapter->hw, IXGBE_FCTRL);
-                       u32 rmcs = IXGBE_READ_REG(&adapter->hw, IXGBE_RMCS);
+                       u32 frctl = IXGBE_READ_REG(hw, IXGBE_FCTRL);
+                       u32 rmcs = IXGBE_READ_REG(hw, IXGBE_RMCS);
 #define FLOW_RX (frctl & IXGBE_FCTRL_RFCE)
 #define FLOW_TX (rmcs & IXGBE_RMCS_TFCE_802_3X)
                        DPRINTK(LINK, INFO, "NIC Link is Up %s, "
-                               "Flow Control: %s\n",
-                               (link_speed == IXGBE_LINK_SPEED_10GB_FULL ?
-                                "10 Gbps" :
-                                (link_speed == IXGBE_LINK_SPEED_1GB_FULL ?
-                                 "1 Gbps" : "unknown speed")),
-                               ((FLOW_RX && FLOW_TX) ? "RX/TX" :
-                                (FLOW_RX ? "RX" :
-                                (FLOW_TX ? "TX" : "None"))));
+                               "Flow Control: %s\n",
+                               (link_speed == IXGBE_LINK_SPEED_10GB_FULL ?
+                                "10 Gbps" :
+                                (link_speed == IXGBE_LINK_SPEED_1GB_FULL ?
+                                 "1 Gbps" : "unknown speed")),
+                               ((FLOW_RX && FLOW_TX) ? "RX/TX" :
+                                (FLOW_RX ? "RX" :
+                                (FLOW_TX ? "TX" : "None"))));
 
                        netif_carrier_on(netdev);
                        netif_tx_wake_all_queues(netdev);
@@ -3039,6 +3096,8 @@ static void ixgbe_watchdog(unsigned long data)
                        adapter->detect_tx_hung = true;
                }
        } else {
+               adapter->link_up = false;
+               adapter->link_speed = 0;
                if (netif_carrier_ok(netdev)) {
                        DPRINTK(LINK, INFO, "NIC Link is Down\n");
                        netif_carrier_off(netdev);
@@ -3047,24 +3106,7 @@ static void ixgbe_watchdog(unsigned long data)
        }
 
        ixgbe_update_stats(adapter);
-
-       if (!test_bit(__IXGBE_DOWN, &adapter->state)) {
-               /* Cause software interrupt to ensure rx rings are cleaned */
-               if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
-                       u32 eics =
-                        (1 << (adapter->num_msix_vectors - NON_Q_VECTORS)) - 1;
-                       IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, eics);
-               } else {
-                       /* for legacy and MSI interrupts don't set any bits that
-                        * are enabled for EIAM, because this operation would
-                        * set *both* EIMS and EICS for any bit in EIAM */
-                       IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
-                                    (IXGBE_EICS_TCP_TIMER | IXGBE_EICS_OTHER));
-               }
-               /* Reset the timer */
-               mod_timer(&adapter->watchdog_timer,
-                         round_jiffies(jiffies + 2 * HZ));
-       }
+       adapter->flags &= ~IXGBE_FLAG_IN_WATCHDOG_TASK;
 }
 
 static int ixgbe_tso(struct ixgbe_adapter *adapter,
@@ -3707,6 +3749,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
        adapter->watchdog_timer.data = (unsigned long)adapter;
 
        INIT_WORK(&adapter->reset_task, ixgbe_reset_task);
+       INIT_WORK(&adapter->watchdog_task, ixgbe_watchdog_task);
 
        err = ixgbe_init_interrupt_scheme(adapter);
        if (err)