|
@@ -98,6 +98,23 @@ static int debug = -1;
|
|
|
module_param(debug, int, 0);
|
|
|
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
|
|
|
|
|
|
+static void ixgbevf_service_event_schedule(struct ixgbevf_adapter *adapter)
|
|
|
+{
|
|
|
+ if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
|
|
|
+ !test_bit(__IXGBEVF_REMOVING, &adapter->state) &&
|
|
|
+ !test_and_set_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state))
|
|
|
+ schedule_work(&adapter->service_task);
|
|
|
+}
|
|
|
+
|
|
|
+static void ixgbevf_service_event_complete(struct ixgbevf_adapter *adapter)
|
|
|
+{
|
|
|
+ BUG_ON(!test_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state));
|
|
|
+
|
|
|
+ /* flush memory to make sure state is correct before next watchdog */
|
|
|
+ smp_mb__before_atomic();
|
|
|
+ clear_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state);
|
|
|
+}
|
|
|
+
|
|
|
/* forward decls */
|
|
|
static void ixgbevf_queue_reset_subtask(struct ixgbevf_adapter *adapter);
|
|
|
static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector);
|
|
@@ -111,8 +128,8 @@ static void ixgbevf_remove_adapter(struct ixgbe_hw *hw)
|
|
|
return;
|
|
|
hw->hw_addr = NULL;
|
|
|
dev_err(&adapter->pdev->dev, "Adapter removed\n");
|
|
|
- if (test_bit(__IXGBEVF_WORK_INIT, &adapter->state))
|
|
|
- schedule_work(&adapter->watchdog_task);
|
|
|
+ if (test_bit(__IXGBEVF_SERVICE_INITED, &adapter->state))
|
|
|
+ ixgbevf_service_event_schedule(adapter);
|
|
|
}
|
|
|
|
|
|
static void ixgbevf_check_remove(struct ixgbe_hw *hw, u32 reg)
|
|
@@ -246,6 +263,15 @@ static inline bool ixgbevf_check_tx_hang(struct ixgbevf_ring *tx_ring)
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+static void ixgbevf_tx_timeout_reset(struct ixgbevf_adapter *adapter)
|
|
|
+{
|
|
|
+ /* Do the reset outside of interrupt context */
|
|
|
+ if (!test_bit(__IXGBEVF_DOWN, &adapter->state)) {
|
|
|
+ adapter->flags |= IXGBEVF_FLAG_RESET_REQUESTED;
|
|
|
+ ixgbevf_service_event_schedule(adapter);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* ixgbevf_tx_timeout - Respond to a Tx Hang
|
|
|
* @netdev: network interface device structure
|
|
@@ -254,8 +280,7 @@ static void ixgbevf_tx_timeout(struct net_device *netdev)
|
|
|
{
|
|
|
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
|
|
|
|
|
|
- /* Do the reset outside of interrupt context */
|
|
|
- schedule_work(&adapter->reset_task);
|
|
|
+ ixgbevf_tx_timeout_reset(adapter);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -387,7 +412,7 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector,
|
|
|
netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
|
|
|
|
|
|
/* schedule immediate reset if we believe we hung */
|
|
|
- schedule_work(&adapter->reset_task);
|
|
|
+ ixgbevf_tx_timeout_reset(adapter);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -1239,9 +1264,7 @@ static irqreturn_t ixgbevf_msix_other(int irq, void *data)
|
|
|
|
|
|
hw->mac.get_link_status = 1;
|
|
|
|
|
|
- if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
|
|
|
- !test_bit(__IXGBEVF_REMOVING, &adapter->state))
|
|
|
- mod_timer(&adapter->watchdog_timer, jiffies);
|
|
|
+ ixgbevf_service_event_schedule(adapter);
|
|
|
|
|
|
IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, adapter->eims_other);
|
|
|
|
|
@@ -2051,7 +2074,7 @@ static void ixgbevf_up_complete(struct ixgbevf_adapter *adapter)
|
|
|
ixgbevf_init_last_counter_stats(adapter);
|
|
|
|
|
|
hw->mac.get_link_status = 1;
|
|
|
- mod_timer(&adapter->watchdog_timer, jiffies);
|
|
|
+ mod_timer(&adapter->service_timer, jiffies);
|
|
|
}
|
|
|
|
|
|
void ixgbevf_up(struct ixgbevf_adapter *adapter)
|
|
@@ -2177,13 +2200,7 @@ void ixgbevf_down(struct ixgbevf_adapter *adapter)
|
|
|
|
|
|
ixgbevf_napi_disable_all(adapter);
|
|
|
|
|
|
- del_timer_sync(&adapter->watchdog_timer);
|
|
|
-
|
|
|
- /* can't call flush scheduled work here because it can deadlock
|
|
|
- * if linkwatch_event tries to acquire the rtnl_lock which we are
|
|
|
- * holding */
|
|
|
- while (adapter->flags & IXGBE_FLAG_IN_WATCHDOG_TASK)
|
|
|
- msleep(1);
|
|
|
+ del_timer_sync(&adapter->service_timer);
|
|
|
|
|
|
/* disable transmits in the hardware now that interrupts are off */
|
|
|
for (i = 0; i < adapter->num_tx_queues; i++) {
|
|
@@ -2711,22 +2728,25 @@ void ixgbevf_update_stats(struct ixgbevf_adapter *adapter)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * ixgbevf_watchdog - Timer Call-back
|
|
|
+ * ixgbevf_service_timer - Timer Call-back
|
|
|
* @data: pointer to adapter cast into an unsigned long
|
|
|
**/
|
|
|
-static void ixgbevf_watchdog(unsigned long data)
|
|
|
+static void ixgbevf_service_timer(unsigned long data)
|
|
|
{
|
|
|
struct ixgbevf_adapter *adapter = (struct ixgbevf_adapter *)data;
|
|
|
|
|
|
- /* Do the reset outside of interrupt context */
|
|
|
- schedule_work(&adapter->watchdog_task);
|
|
|
+ /* Reset the timer */
|
|
|
+ mod_timer(&adapter->service_timer, (HZ * 2) + jiffies);
|
|
|
+
|
|
|
+ ixgbevf_service_event_schedule(adapter);
|
|
|
}
|
|
|
|
|
|
-static void ixgbevf_reset_task(struct work_struct *work)
|
|
|
+static void ixgbevf_reset_subtask(struct ixgbevf_adapter *adapter)
|
|
|
{
|
|
|
- struct ixgbevf_adapter *adapter;
|
|
|
+ if (!(adapter->flags & IXGBEVF_FLAG_RESET_REQUESTED))
|
|
|
+ return;
|
|
|
|
|
|
- adapter = container_of(work, struct ixgbevf_adapter, reset_task);
|
|
|
+ adapter->flags &= ~IXGBEVF_FLAG_RESET_REQUESTED;
|
|
|
|
|
|
/* If we're already down or resetting, just bail */
|
|
|
if (test_bit(__IXGBEVF_DOWN, &adapter->state) ||
|
|
@@ -2766,6 +2786,7 @@ static void ixgbevf_check_hang_subtask(struct ixgbevf_adapter *adapter)
|
|
|
/* get one bit for every active tx/rx interrupt vector */
|
|
|
for (i = 0; i < adapter->num_msix_vectors - NON_Q_VECTORS; i++) {
|
|
|
struct ixgbevf_q_vector *qv = adapter->q_vector[i];
|
|
|
+
|
|
|
if (qv->rx.ring || qv->tx.ring)
|
|
|
eics |= 1 << i;
|
|
|
}
|
|
@@ -2793,7 +2814,7 @@ static void ixgbevf_watchdog_update_link(struct ixgbevf_adapter *adapter)
|
|
|
|
|
|
/* if check for link returns error we will need to reset */
|
|
|
if (err && time_after(jiffies, adapter->last_reset + (10 * HZ))) {
|
|
|
- schedule_work(&adapter->reset_task);
|
|
|
+ adapter->flags |= IXGBEVF_FLAG_RESET_REQUESTED;
|
|
|
link_up = false;
|
|
|
}
|
|
|
|
|
@@ -2847,14 +2868,35 @@ static void ixgbevf_watchdog_link_is_down(struct ixgbevf_adapter *adapter)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * ixgbevf_watchdog_task - worker thread to bring link up
|
|
|
+ * ixgbevf_watchdog_subtask - worker thread to bring link up
|
|
|
+ * @work: pointer to work_struct containing our data
|
|
|
+ **/
|
|
|
+static void ixgbevf_watchdog_subtask(struct ixgbevf_adapter *adapter)
|
|
|
+{
|
|
|
+ /* if interface is down do nothing */
|
|
|
+ if (test_bit(__IXGBEVF_DOWN, &adapter->state) ||
|
|
|
+ test_bit(__IXGBEVF_RESETTING, &adapter->state))
|
|
|
+ return;
|
|
|
+
|
|
|
+ ixgbevf_watchdog_update_link(adapter);
|
|
|
+
|
|
|
+ if (adapter->link_up)
|
|
|
+ ixgbevf_watchdog_link_is_up(adapter);
|
|
|
+ else
|
|
|
+ ixgbevf_watchdog_link_is_down(adapter);
|
|
|
+
|
|
|
+ ixgbevf_update_stats(adapter);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ixgbevf_service_task - manages and runs subtasks
|
|
|
* @work: pointer to work_struct containing our data
|
|
|
**/
|
|
|
-static void ixgbevf_watchdog_task(struct work_struct *work)
|
|
|
+static void ixgbevf_service_task(struct work_struct *work)
|
|
|
{
|
|
|
struct ixgbevf_adapter *adapter = container_of(work,
|
|
|
struct ixgbevf_adapter,
|
|
|
- watchdog_task);
|
|
|
+ service_task);
|
|
|
struct ixgbe_hw *hw = &adapter->hw;
|
|
|
|
|
|
if (IXGBE_REMOVED(hw->hw_addr)) {
|
|
@@ -2867,27 +2909,11 @@ static void ixgbevf_watchdog_task(struct work_struct *work)
|
|
|
}
|
|
|
|
|
|
ixgbevf_queue_reset_subtask(adapter);
|
|
|
-
|
|
|
- adapter->flags |= IXGBE_FLAG_IN_WATCHDOG_TASK;
|
|
|
-
|
|
|
- ixgbevf_watchdog_update_link(adapter);
|
|
|
-
|
|
|
- if (adapter->link_up)
|
|
|
- ixgbevf_watchdog_link_is_up(adapter);
|
|
|
- else
|
|
|
- ixgbevf_watchdog_link_is_down(adapter);
|
|
|
-
|
|
|
- ixgbevf_update_stats(adapter);
|
|
|
-
|
|
|
+ ixgbevf_reset_subtask(adapter);
|
|
|
+ ixgbevf_watchdog_subtask(adapter);
|
|
|
ixgbevf_check_hang_subtask(adapter);
|
|
|
|
|
|
- /* Reset the timer */
|
|
|
- if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
|
|
|
- !test_bit(__IXGBEVF_REMOVING, &adapter->state))
|
|
|
- mod_timer(&adapter->watchdog_timer,
|
|
|
- round_jiffies(jiffies + (2 * HZ)));
|
|
|
-
|
|
|
- adapter->flags &= ~IXGBE_FLAG_IN_WATCHDOG_TASK;
|
|
|
+ ixgbevf_service_event_complete(adapter);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -3994,17 +4020,17 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
|
|
|
|
netdev->priv_flags |= IFF_UNICAST_FLT;
|
|
|
|
|
|
- init_timer(&adapter->watchdog_timer);
|
|
|
- adapter->watchdog_timer.function = ixgbevf_watchdog;
|
|
|
- adapter->watchdog_timer.data = (unsigned long)adapter;
|
|
|
-
|
|
|
if (IXGBE_REMOVED(hw->hw_addr)) {
|
|
|
err = -EIO;
|
|
|
goto err_sw_init;
|
|
|
}
|
|
|
- INIT_WORK(&adapter->reset_task, ixgbevf_reset_task);
|
|
|
- INIT_WORK(&adapter->watchdog_task, ixgbevf_watchdog_task);
|
|
|
- set_bit(__IXGBEVF_WORK_INIT, &adapter->state);
|
|
|
+
|
|
|
+ setup_timer(&adapter->service_timer, &ixgbevf_service_timer,
|
|
|
+ (unsigned long)adapter);
|
|
|
+
|
|
|
+ INIT_WORK(&adapter->service_task, ixgbevf_service_task);
|
|
|
+ set_bit(__IXGBEVF_SERVICE_INITED, &adapter->state);
|
|
|
+ clear_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state);
|
|
|
|
|
|
err = ixgbevf_init_interrupt_scheme(adapter);
|
|
|
if (err)
|
|
@@ -4078,11 +4104,7 @@ static void ixgbevf_remove(struct pci_dev *pdev)
|
|
|
adapter = netdev_priv(netdev);
|
|
|
|
|
|
set_bit(__IXGBEVF_REMOVING, &adapter->state);
|
|
|
-
|
|
|
- del_timer_sync(&adapter->watchdog_timer);
|
|
|
-
|
|
|
- cancel_work_sync(&adapter->reset_task);
|
|
|
- cancel_work_sync(&adapter->watchdog_task);
|
|
|
+ cancel_work_sync(&adapter->service_task);
|
|
|
|
|
|
if (netdev->reg_state == NETREG_REGISTERED)
|
|
|
unregister_netdev(netdev);
|
|
@@ -4116,7 +4138,7 @@ static pci_ers_result_t ixgbevf_io_error_detected(struct pci_dev *pdev,
|
|
|
struct net_device *netdev = pci_get_drvdata(pdev);
|
|
|
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
|
|
|
|
|
|
- if (!test_bit(__IXGBEVF_WORK_INIT, &adapter->state))
|
|
|
+ if (!test_bit(__IXGBEVF_SERVICE_INITED, &adapter->state))
|
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
|
|
|
|
rtnl_lock();
|