Commit 371e8bc2 authored by Francois Romieu's avatar Francois Romieu Committed by Francois Romieu

8139too: fix a TX timeout watchdog thread against NAPI softirq race

Ingo's stealth lock validator detected that both thread acquire
dev->xmit_lock and tp->rx_lock in reverse order.
Signed-off-by: default avatarFrancois Romieu <romieu@fr.zoreil.com>
parent 8351538d
...@@ -586,6 +586,7 @@ struct rtl8139_private { ...@@ -586,6 +586,7 @@ struct rtl8139_private {
dma_addr_t tx_bufs_dma; dma_addr_t tx_bufs_dma;
signed char phys[4]; /* MII device addresses. */ signed char phys[4]; /* MII device addresses. */
char twistie, twist_row, twist_col; /* Twister tune state. */ char twistie, twist_row, twist_col; /* Twister tune state. */
unsigned int watchdog_fired : 1;
unsigned int default_port : 4; /* Last dev->if_port value. */ unsigned int default_port : 4; /* Last dev->if_port value. */
unsigned int have_thread : 1; unsigned int have_thread : 1;
spinlock_t lock; spinlock_t lock;
...@@ -638,6 +639,7 @@ static void rtl8139_set_rx_mode (struct net_device *dev); ...@@ -638,6 +639,7 @@ static void rtl8139_set_rx_mode (struct net_device *dev);
static void __set_rx_mode (struct net_device *dev); static void __set_rx_mode (struct net_device *dev);
static void rtl8139_hw_start (struct net_device *dev); static void rtl8139_hw_start (struct net_device *dev);
static void rtl8139_thread (void *_data); static void rtl8139_thread (void *_data);
static void rtl8139_tx_timeout_task(void *_data);
static struct ethtool_ops rtl8139_ethtool_ops; static struct ethtool_ops rtl8139_ethtool_ops;
/* write MMIO register, with flush */ /* write MMIO register, with flush */
...@@ -1598,13 +1600,14 @@ static void rtl8139_thread (void *_data) ...@@ -1598,13 +1600,14 @@ static void rtl8139_thread (void *_data)
{ {
struct net_device *dev = _data; struct net_device *dev = _data;
struct rtl8139_private *tp = netdev_priv(dev); struct rtl8139_private *tp = netdev_priv(dev);
unsigned long thr_delay; unsigned long thr_delay = next_tick;
if (rtnl_shlock_nowait() == 0) { if (tp->watchdog_fired) {
tp->watchdog_fired = 0;
rtl8139_tx_timeout_task(_data);
} else if (rtnl_shlock_nowait() == 0) {
rtl8139_thread_iter (dev, tp, tp->mmio_addr); rtl8139_thread_iter (dev, tp, tp->mmio_addr);
rtnl_unlock (); rtnl_unlock ();
thr_delay = next_tick;
} else { } else {
/* unlikely race. mitigate with fast poll. */ /* unlikely race. mitigate with fast poll. */
thr_delay = HZ / 2; thr_delay = HZ / 2;
...@@ -1631,7 +1634,8 @@ static void rtl8139_stop_thread(struct rtl8139_private *tp) ...@@ -1631,7 +1634,8 @@ static void rtl8139_stop_thread(struct rtl8139_private *tp)
if (tp->have_thread) { if (tp->have_thread) {
cancel_rearming_delayed_work(&tp->thread); cancel_rearming_delayed_work(&tp->thread);
tp->have_thread = 0; tp->have_thread = 0;
} } else
flush_scheduled_work();
} }
static inline void rtl8139_tx_clear (struct rtl8139_private *tp) static inline void rtl8139_tx_clear (struct rtl8139_private *tp)
...@@ -1642,14 +1646,13 @@ static inline void rtl8139_tx_clear (struct rtl8139_private *tp) ...@@ -1642,14 +1646,13 @@ static inline void rtl8139_tx_clear (struct rtl8139_private *tp)
/* XXX account for unsent Tx packets in tp->stats.tx_dropped */ /* XXX account for unsent Tx packets in tp->stats.tx_dropped */
} }
static void rtl8139_tx_timeout_task (void *_data)
static void rtl8139_tx_timeout (struct net_device *dev)
{ {
struct net_device *dev = _data;
struct rtl8139_private *tp = netdev_priv(dev); struct rtl8139_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr; void __iomem *ioaddr = tp->mmio_addr;
int i; int i;
u8 tmp8; u8 tmp8;
unsigned long flags;
printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x " printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x "
"media %2.2x.\n", dev->name, RTL_R8 (ChipCmd), "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd),
...@@ -1670,23 +1673,34 @@ static void rtl8139_tx_timeout (struct net_device *dev) ...@@ -1670,23 +1673,34 @@ static void rtl8139_tx_timeout (struct net_device *dev)
if (tmp8 & CmdTxEnb) if (tmp8 & CmdTxEnb)
RTL_W8 (ChipCmd, CmdRxEnb); RTL_W8 (ChipCmd, CmdRxEnb);
spin_lock(&tp->rx_lock); spin_lock_bh(&tp->rx_lock);
/* Disable interrupts by clearing the interrupt mask. */ /* Disable interrupts by clearing the interrupt mask. */
RTL_W16 (IntrMask, 0x0000); RTL_W16 (IntrMask, 0x0000);
/* Stop a shared interrupt from scavenging while we are. */ /* Stop a shared interrupt from scavenging while we are. */
spin_lock_irqsave (&tp->lock, flags); spin_lock_irq(&tp->lock);
rtl8139_tx_clear (tp); rtl8139_tx_clear (tp);
spin_unlock_irqrestore (&tp->lock, flags); spin_unlock_irq(&tp->lock);
/* ...and finally, reset everything */ /* ...and finally, reset everything */
if (netif_running(dev)) { if (netif_running(dev)) {
rtl8139_hw_start (dev); rtl8139_hw_start (dev);
netif_wake_queue (dev); netif_wake_queue (dev);
} }
spin_unlock(&tp->rx_lock); spin_unlock_bh(&tp->rx_lock);
} }
static void rtl8139_tx_timeout (struct net_device *dev)
{
struct rtl8139_private *tp = netdev_priv(dev);
if (!tp->have_thread) {
INIT_WORK(&tp->thread, rtl8139_tx_timeout_task, dev);
schedule_delayed_work(&tp->thread, next_tick);
} else
tp->watchdog_fired = 1;
}
static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
{ {
......
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