Commit cf8280ee authored by Jesse Brandeburg's avatar Jesse Brandeburg Committed by Jeff Garzik

ixgbe: Update watchdog thread to accomodate longerlink_up events

This patch updates the link_up code and watchdog thread so that link_up
doesn't cause stack overflows due to long waits in interrupt context.
Signed-off-by: default avatarJesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: default avatarPeter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent ce94bf46
...@@ -309,6 +309,12 @@ struct ixgbe_adapter { ...@@ -309,6 +309,12 @@ struct ixgbe_adapter {
u64 lro_aggregated; u64 lro_aggregated;
u64 lro_flushed; u64 lro_flushed;
u64 lro_no_desc; u64 lro_no_desc;
u32 link_speed;
bool link_up;
unsigned long link_check_timeout;
struct work_struct watchdog_task;
}; };
enum ixbge_state_t { enum ixbge_state_t {
......
...@@ -47,7 +47,8 @@ static s32 ixgbe_get_copper_link_settings_82598(struct ixgbe_hw *hw, ...@@ -47,7 +47,8 @@ static s32 ixgbe_get_copper_link_settings_82598(struct ixgbe_hw *hw,
static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw); static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw);
static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw); static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw);
static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, u32 *speed, static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, u32 *speed,
bool *link_up); bool *link_up,
bool link_up_wait_to_complete);
static s32 ixgbe_setup_mac_link_speed_82598(struct ixgbe_hw *hw, u32 speed, static s32 ixgbe_setup_mac_link_speed_82598(struct ixgbe_hw *hw, u32 speed,
bool autoneg, bool autoneg,
bool autoneg_wait_to_complete); bool autoneg_wait_to_complete);
...@@ -277,20 +278,36 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw) ...@@ -277,20 +278,36 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw)
* @hw: pointer to hardware structure * @hw: pointer to hardware structure
* @speed: pointer to link speed * @speed: pointer to link speed
* @link_up: true is link is up, false otherwise * @link_up: true is link is up, false otherwise
* @link_up_wait_to_complete: bool used to wait for link up or not
* *
* Reads the links register to determine if link is up and the current speed * Reads the links register to determine if link is up and the current speed
**/ **/
static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, u32 *speed, static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, u32 *speed,
bool *link_up) bool *link_up,
bool link_up_wait_to_complete)
{ {
u32 links_reg; u32 links_reg;
u32 i;
links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS); links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS);
if (links_reg & IXGBE_LINKS_UP) if (link_up_wait_to_complete) {
*link_up = true; for (i = 0; i < IXGBE_LINK_UP_TIME; i++) {
else if (links_reg & IXGBE_LINKS_UP) {
*link_up = false; *link_up = true;
break;
} else {
*link_up = false;
}
msleep(100);
links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS);
}
} else {
if (links_reg & IXGBE_LINKS_UP)
*link_up = true;
else
*link_up = false;
}
if (links_reg & IXGBE_LINKS_SPEED) if (links_reg & IXGBE_LINKS_SPEED)
*speed = IXGBE_LINK_SPEED_10GB_FULL; *speed = IXGBE_LINK_SPEED_10GB_FULL;
......
...@@ -130,7 +130,7 @@ static int ixgbe_get_settings(struct net_device *netdev, ...@@ -130,7 +130,7 @@ static int ixgbe_get_settings(struct net_device *netdev,
ecmd->port = PORT_FIBRE; ecmd->port = PORT_FIBRE;
} }
adapter->hw.mac.ops.check_link(hw, &(link_speed), &link_up); adapter->hw.mac.ops.check_link(hw, &(link_speed), &link_up, false);
if (link_up) { if (link_up) {
ecmd->speed = (link_speed == IXGBE_LINK_SPEED_10GB_FULL) ? ecmd->speed = (link_speed == IXGBE_LINK_SPEED_10GB_FULL) ?
SPEED_10000 : SPEED_1000; SPEED_10000 : SPEED_1000;
......
...@@ -902,6 +902,20 @@ static void ixgbe_set_itr_msix(struct ixgbe_q_vector *q_vector) ...@@ -902,6 +902,20 @@ static void ixgbe_set_itr_msix(struct ixgbe_q_vector *q_vector)
return; 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) static irqreturn_t ixgbe_msix_lsc(int irq, void *data)
{ {
struct net_device *netdev = data; struct net_device *netdev = data;
...@@ -909,11 +923,8 @@ static irqreturn_t ixgbe_msix_lsc(int irq, void *data) ...@@ -909,11 +923,8 @@ static irqreturn_t ixgbe_msix_lsc(int irq, void *data)
struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw *hw = &adapter->hw;
u32 eicr = IXGBE_READ_REG(hw, IXGBE_EICR); u32 eicr = IXGBE_READ_REG(hw, IXGBE_EICR);
if (eicr & IXGBE_EICR_LSC) { if (eicr & IXGBE_EICR_LSC)
adapter->lsc_int++; ixgbe_check_lsc(adapter);
if (!test_bit(__IXGBE_DOWN, &adapter->state))
mod_timer(&adapter->watchdog_timer, jiffies);
}
if (!test_bit(__IXGBE_DOWN, &adapter->state)) if (!test_bit(__IXGBE_DOWN, &adapter->state))
IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_OTHER); IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_OTHER);
...@@ -1237,12 +1248,8 @@ static irqreturn_t ixgbe_intr(int irq, void *data) ...@@ -1237,12 +1248,8 @@ static irqreturn_t ixgbe_intr(int irq, void *data)
if (!eicr) if (!eicr)
return IRQ_NONE; /* Not our interrupt */ return IRQ_NONE; /* Not our interrupt */
if (eicr & IXGBE_EICR_LSC) { if (eicr & IXGBE_EICR_LSC)
adapter->lsc_int++; ixgbe_check_lsc(adapter);
if (!test_bit(__IXGBE_DOWN, &adapter->state))
mod_timer(&adapter->watchdog_timer, jiffies);
}
if (netif_rx_schedule_prep(netdev, &adapter->q_vector[0].napi)) { if (netif_rx_schedule_prep(netdev, &adapter->q_vector[0].napi)) {
adapter->tx_ring[0].total_packets = 0; adapter->tx_ring[0].total_packets = 0;
...@@ -1897,6 +1904,8 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter) ...@@ -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 /* bring the link up in the watchdog, this could race with our first
* link up interrupt but shouldn't be a problem */ * 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); mod_timer(&adapter->watchdog_timer, jiffies);
return 0; return 0;
} }
...@@ -2098,6 +2107,7 @@ void ixgbe_down(struct ixgbe_adapter *adapter) ...@@ -2098,6 +2107,7 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
ixgbe_napi_disable_all(adapter); ixgbe_napi_disable_all(adapter);
del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->watchdog_timer);
cancel_work_sync(&adapter->watchdog_task);
netif_carrier_off(netdev); netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev); netif_tx_stop_all_queues(netdev);
...@@ -3010,27 +3020,74 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter) ...@@ -3010,27 +3020,74 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter)
static void ixgbe_watchdog(unsigned long data) static void ixgbe_watchdog(unsigned long data)
{ {
struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data; struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data;
struct net_device *netdev = adapter->netdev; struct ixgbe_hw *hw = &adapter->hw;
bool link_up;
u32 link_speed = 0; /* 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 (link_up) {
if (!netif_carrier_ok(netdev)) { if (!netif_carrier_ok(netdev)) {
u32 frctl = IXGBE_READ_REG(&adapter->hw, IXGBE_FCTRL); u32 frctl = IXGBE_READ_REG(hw, IXGBE_FCTRL);
u32 rmcs = IXGBE_READ_REG(&adapter->hw, IXGBE_RMCS); u32 rmcs = IXGBE_READ_REG(hw, IXGBE_RMCS);
#define FLOW_RX (frctl & IXGBE_FCTRL_RFCE) #define FLOW_RX (frctl & IXGBE_FCTRL_RFCE)
#define FLOW_TX (rmcs & IXGBE_RMCS_TFCE_802_3X) #define FLOW_TX (rmcs & IXGBE_RMCS_TFCE_802_3X)
DPRINTK(LINK, INFO, "NIC Link is Up %s, " DPRINTK(LINK, INFO, "NIC Link is Up %s, "
"Flow Control: %s\n", "Flow Control: %s\n",
(link_speed == IXGBE_LINK_SPEED_10GB_FULL ? (link_speed == IXGBE_LINK_SPEED_10GB_FULL ?
"10 Gbps" : "10 Gbps" :
(link_speed == IXGBE_LINK_SPEED_1GB_FULL ? (link_speed == IXGBE_LINK_SPEED_1GB_FULL ?
"1 Gbps" : "unknown speed")), "1 Gbps" : "unknown speed")),
((FLOW_RX && FLOW_TX) ? "RX/TX" : ((FLOW_RX && FLOW_TX) ? "RX/TX" :
(FLOW_RX ? "RX" : (FLOW_RX ? "RX" :
(FLOW_TX ? "TX" : "None")))); (FLOW_TX ? "TX" : "None"))));
netif_carrier_on(netdev); netif_carrier_on(netdev);
netif_tx_wake_all_queues(netdev); netif_tx_wake_all_queues(netdev);
...@@ -3039,6 +3096,8 @@ static void ixgbe_watchdog(unsigned long data) ...@@ -3039,6 +3096,8 @@ static void ixgbe_watchdog(unsigned long data)
adapter->detect_tx_hung = true; adapter->detect_tx_hung = true;
} }
} else { } else {
adapter->link_up = false;
adapter->link_speed = 0;
if (netif_carrier_ok(netdev)) { if (netif_carrier_ok(netdev)) {
DPRINTK(LINK, INFO, "NIC Link is Down\n"); DPRINTK(LINK, INFO, "NIC Link is Down\n");
netif_carrier_off(netdev); netif_carrier_off(netdev);
...@@ -3047,24 +3106,7 @@ static void ixgbe_watchdog(unsigned long data) ...@@ -3047,24 +3106,7 @@ static void ixgbe_watchdog(unsigned long data)
} }
ixgbe_update_stats(adapter); ixgbe_update_stats(adapter);
adapter->flags &= ~IXGBE_FLAG_IN_WATCHDOG_TASK;
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));
}
} }
static int ixgbe_tso(struct ixgbe_adapter *adapter, static int ixgbe_tso(struct ixgbe_adapter *adapter,
...@@ -3707,6 +3749,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, ...@@ -3707,6 +3749,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
adapter->watchdog_timer.data = (unsigned long)adapter; adapter->watchdog_timer.data = (unsigned long)adapter;
INIT_WORK(&adapter->reset_task, ixgbe_reset_task); INIT_WORK(&adapter->reset_task, ixgbe_reset_task);
INIT_WORK(&adapter->watchdog_task, ixgbe_watchdog_task);
err = ixgbe_init_interrupt_scheme(adapter); err = ixgbe_init_interrupt_scheme(adapter);
if (err) if (err)
......
...@@ -703,6 +703,7 @@ ...@@ -703,6 +703,7 @@
#define IXGBE_LINKS_TL_FAULT 0x00001000 #define IXGBE_LINKS_TL_FAULT 0x00001000
#define IXGBE_LINKS_SIGNAL 0x00000F00 #define IXGBE_LINKS_SIGNAL 0x00000F00
#define IXGBE_LINK_UP_TIME 90 /* 9.0 Seconds */
#define IXGBE_AUTO_NEG_TIME 45 /* 4.5 Seconds */ #define IXGBE_AUTO_NEG_TIME 45 /* 4.5 Seconds */
/* SW Semaphore Register bitmasks */ /* SW Semaphore Register bitmasks */
...@@ -1249,7 +1250,7 @@ struct ixgbe_mac_operations { ...@@ -1249,7 +1250,7 @@ struct ixgbe_mac_operations {
s32 (*reset)(struct ixgbe_hw *); s32 (*reset)(struct ixgbe_hw *);
enum ixgbe_media_type (*get_media_type)(struct ixgbe_hw *); enum ixgbe_media_type (*get_media_type)(struct ixgbe_hw *);
s32 (*setup_link)(struct ixgbe_hw *); s32 (*setup_link)(struct ixgbe_hw *);
s32 (*check_link)(struct ixgbe_hw *, u32 *, bool *); s32 (*check_link)(struct ixgbe_hw *, u32 *, bool *, bool);
s32 (*setup_link_speed)(struct ixgbe_hw *, u32, bool, bool); s32 (*setup_link_speed)(struct ixgbe_hw *, u32, bool, bool);
s32 (*get_link_settings)(struct ixgbe_hw *, u32 *, bool *); s32 (*get_link_settings)(struct ixgbe_hw *, u32 *, bool *);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment