Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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 {
+23 −6
Original line number Diff line number Diff line
@@ -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);
@@ -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 (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;
+1 −1
Original line number Diff line number Diff line
@@ -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;
+86 −43
Original line number Diff line number Diff line
@@ -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,16 +3020,63 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter)
static void ixgbe_watchdog(unsigned long data)
{
	struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data;
	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));
	}

	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;
	bool link_up;
	u32 link_speed = 0;
	struct ixgbe_hw *hw = &adapter->hw;
	u32 link_speed = adapter->link_speed;
	bool link_up = adapter->link_up;

	adapter->hw.mac.ops.check_link(&adapter->hw, &(link_speed), &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, "
@@ -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)
+2 −1
Original line number Diff line number Diff line
@@ -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 */
@@ -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 *);
};