Skip to content

Commit

Permalink
ixgbe: Update watchdog thread to accomodate longerlink_up events
Browse files Browse the repository at this point in the history
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: Jesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: Peter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
  • Loading branch information
Jesse Brandeburg authored and Jeff Garzik committed Sep 24, 2008
1 parent ce94bf4 commit cf8280e
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 51 deletions.
6 changes: 6 additions & 0 deletions drivers/net/ixgbe/ixgbe.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@ struct ixgbe_adapter {
u64 lro_aggregated;
u64 lro_flushed;
u64 lro_no_desc;

u32 link_speed;
bool link_up;
unsigned long link_check_timeout;

struct work_struct watchdog_task;
};

enum ixbge_state_t {
Expand Down
29 changes: 23 additions & 6 deletions drivers/net/ixgbe/ixgbe_82598.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw);
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,
bool autoneg,
bool autoneg_wait_to_complete);
Expand Down Expand Up @@ -277,20 +278,36 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw)
* @hw: pointer to hardware structure
* @speed: pointer to link speed
* @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
**/
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 i;

links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS);

if (links_reg & IXGBE_LINKS_UP)
*link_up = true;
else
*link_up = false;
if (link_up_wait_to_complete) {
for (i = 0; i < IXGBE_LINK_UP_TIME; i++) {
if (links_reg & IXGBE_LINKS_UP) {
*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)
*speed = IXGBE_LINK_SPEED_10GB_FULL;
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/ixgbe/ixgbe_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ static int ixgbe_get_settings(struct net_device *netdev,
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) {
ecmd->speed = (link_speed == IXGBE_LINK_SPEED_10GB_FULL) ?
SPEED_10000 : SPEED_1000;
Expand Down
129 changes: 86 additions & 43 deletions drivers/net/ixgbe/ixgbe_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -902,18 +902,29 @@ 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;
struct ixgbe_adapter *adapter = netdev_priv(netdev);
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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/ixgbe/ixgbe_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,7 @@
#define IXGBE_LINKS_TL_FAULT 0x00001000
#define IXGBE_LINKS_SIGNAL 0x00000F00

#define IXGBE_LINK_UP_TIME 90 /* 9.0 Seconds */
#define IXGBE_AUTO_NEG_TIME 45 /* 4.5 Seconds */

/* SW Semaphore Register bitmasks */
Expand Down Expand Up @@ -1249,7 +1250,7 @@ struct ixgbe_mac_operations {
s32 (*reset)(struct ixgbe_hw *);
enum ixgbe_media_type (*get_media_type)(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 (*get_link_settings)(struct ixgbe_hw *, u32 *, bool *);
};
Expand Down

0 comments on commit cf8280e

Please sign in to comment.