mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 19:56:18 +00:00
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: Francois Romieu <romieu@fr.zoreil.com>
This commit is contained in:
parent
8351538db6
commit
371e8bc2af
1 changed files with 26 additions and 12 deletions
|
@ -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);
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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)
|
||||||
/* 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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue