Skip to contentSkip to conversation

e1000: avoid sleeping in watchdog timer context

PATCH In review John W. Linville 2 participants 2 messages Mar 14, 2005 – Mar 22, 2005

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 e1000: avoid sleeping in watchdog timer context

Move bulk of e1000_watchdog to a workqueue to make it safe to call functions which can sleep.

---
The e1000 driver uses a timer to invoke e1000_watchdog(). e1000_watchdog() calls e1000_check_for_link() which can call e1000_config_dsp_after_link_change(). This in turn can call msec_delay(). msec_delay() is, of course, simply a wrapper for msleep().

Since a timer does not have a process context, sleeping is impossible. Attempting to do so causes a crash.

The fix is to move the body of e1000_watchdog() to the newly defined e1000_watchdog_task(). This is invoked via the workqueue interface from the new version of e1000_watchdog().

 drivers/net/e1000/e1000.h      |    1 +
 drivers/net/e1000/e1000_main.c |   15 +++++++++++++--
 2 files changed, 14 insertions(+), 2 deletions(-)
drivers/net/e1000/e1000.h renamed from drivers/net/e1000/e1000.h.orig +1 -0
@@ -203,6 +203,7 @@ struct e1000_adapter {
 	spinlock_t stats_lock;
 	atomic_t irq_sem;
 	struct work_struct tx_timeout_task;
+	struct work_struct watchdog_task;
 	uint8_t fc_autoneg;
 
 	struct timer_list blink_timer;
drivers/net/e1000/e1000_main.c renamed from drivers/net/e1000/e1000_main.c.orig +13 -2
@@ -142,6 +142,7 @@ static void e1000_clean_rx_ring(struct e
 static void e1000_set_multi(struct net_device *netdev);
 static void e1000_update_phy_info(unsigned long data);
 static void e1000_watchdog(unsigned long data);
+static void e1000_watchdog_task(struct e1000_adapter *adapter);
 static void e1000_82547_tx_fifo_stall(unsigned long data);
 static int e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
 static struct net_device_stats * e1000_get_stats(struct net_device *netdev);
@@ -574,6 +575,9 @@ e1000_probe(struct pci_dev *pdev,
 	adapter->watchdog_timer.function = &e1000_watchdog;
 	adapter->watchdog_timer.data = (unsigned long) adapter;
 
+	INIT_WORK(&adapter->watchdog_task,
+		(void (*)(void *))e1000_watchdog_task, adapter);
+
 	init_timer(&adapter->phy_info_timer);
 	adapter->phy_info_timer.function = &e1000_update_phy_info;
 	adapter->phy_info_timer.data = (unsigned long) adapter;
@@ -1529,13 +1533,20 @@ e1000_82547_tx_fifo_stall(unsigned long 
 
 /**
  * e1000_watchdog - Timer Call-back
- * @data: pointer to netdev cast into an unsigned long
+ * @data: pointer to adapter cast into an unsigned long
  **/
-
 static void
 e1000_watchdog(unsigned long data)
 {
 	struct e1000_adapter *adapter = (struct e1000_adapter *) data;
+
+	/* Do the rest outside of interrupt context */
+	schedule_work(&adapter->watchdog_task);
+}
+
+static void
+e1000_watchdog_task(struct e1000_adapter *adapter)
+{
 	struct net_device *netdev = adapter->netdev;
 	struct e1000_desc_ring *txdr = &adapter->tx_ring;
 	uint32_t link;
Signature · 2 lines
John W. Linville
[email protected]