diff options
author | Francois Romieu <romieu@fr.zoreil.com> | 2006-01-30 19:04:33 -0500 |
---|---|---|
committer | Francois Romieu <romieu@electric-eye.fr.zoreil.com> | 2006-02-01 16:04:37 -0500 |
commit | 371e8bc2af11b0571982390932bc07b5ffed9aba (patch) | |
tree | bf474e4de38792fc48833f26cabff286c0f50573 /drivers/net/8139too.c | |
parent | 8351538db6613f40089789ec90e1b58304eb7ffd (diff) |
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>
Diffstat (limited to 'drivers/net/8139too.c')
-rw-r--r-- | drivers/net/8139too.c | 38 |
1 files changed, 26 insertions, 12 deletions
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index adfba44dac5a..2beac55b57d6 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c | |||
@@ -586,6 +586,7 @@ struct rtl8139_private { | |||
586 | dma_addr_t tx_bufs_dma; | 586 | dma_addr_t tx_bufs_dma; |
587 | signed char phys[4]; /* MII device addresses. */ | 587 | signed char phys[4]; /* MII device addresses. */ |
588 | char twistie, twist_row, twist_col; /* Twister tune state. */ | 588 | char twistie, twist_row, twist_col; /* Twister tune state. */ |
589 | unsigned int watchdog_fired : 1; | ||
589 | unsigned int default_port : 4; /* Last dev->if_port value. */ | 590 | unsigned int default_port : 4; /* Last dev->if_port value. */ |
590 | unsigned int have_thread : 1; | 591 | unsigned int have_thread : 1; |
591 | spinlock_t lock; | 592 | spinlock_t lock; |
@@ -638,6 +639,7 @@ static void rtl8139_set_rx_mode (struct net_device *dev); | |||
638 | static void __set_rx_mode (struct net_device *dev); | 639 | static void __set_rx_mode (struct net_device *dev); |
639 | static void rtl8139_hw_start (struct net_device *dev); | 640 | static void rtl8139_hw_start (struct net_device *dev); |
640 | static void rtl8139_thread (void *_data); | 641 | static void rtl8139_thread (void *_data); |
642 | static void rtl8139_tx_timeout_task(void *_data); | ||
641 | static struct ethtool_ops rtl8139_ethtool_ops; | 643 | static struct ethtool_ops rtl8139_ethtool_ops; |
642 | 644 | ||
643 | /* write MMIO register, with flush */ | 645 | /* write MMIO register, with flush */ |
@@ -1598,13 +1600,14 @@ static void rtl8139_thread (void *_data) | |||
1598 | { | 1600 | { |
1599 | struct net_device *dev = _data; | 1601 | struct net_device *dev = _data; |
1600 | struct rtl8139_private *tp = netdev_priv(dev); | 1602 | struct rtl8139_private *tp = netdev_priv(dev); |
1601 | unsigned long thr_delay; | 1603 | unsigned long thr_delay = next_tick; |
1602 | 1604 | ||
1603 | if (rtnl_shlock_nowait() == 0) { | 1605 | if (tp->watchdog_fired) { |
1606 | tp->watchdog_fired = 0; | ||
1607 | rtl8139_tx_timeout_task(_data); | ||
1608 | } else if (rtnl_shlock_nowait() == 0) { | ||
1604 | rtl8139_thread_iter (dev, tp, tp->mmio_addr); | 1609 | rtl8139_thread_iter (dev, tp, tp->mmio_addr); |
1605 | rtnl_unlock (); | 1610 | rtnl_unlock (); |
1606 | |||
1607 | thr_delay = next_tick; | ||
1608 | } else { | 1611 | } else { |
1609 | /* unlikely race. mitigate with fast poll. */ | 1612 | /* unlikely race. mitigate with fast poll. */ |
1610 | thr_delay = HZ / 2; | 1613 | thr_delay = HZ / 2; |
@@ -1631,7 +1634,8 @@ static void rtl8139_stop_thread(struct rtl8139_private *tp) | |||
1631 | if (tp->have_thread) { | 1634 | if (tp->have_thread) { |
1632 | cancel_rearming_delayed_work(&tp->thread); | 1635 | cancel_rearming_delayed_work(&tp->thread); |
1633 | tp->have_thread = 0; | 1636 | tp->have_thread = 0; |
1634 | } | 1637 | } else |
1638 | flush_scheduled_work(); | ||
1635 | } | 1639 | } |
1636 | 1640 | ||
1637 | static inline void rtl8139_tx_clear (struct rtl8139_private *tp) | 1641 | 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 | /* XXX account for unsent Tx packets in tp->stats.tx_dropped */ | 1646 | /* XXX account for unsent Tx packets in tp->stats.tx_dropped */ |
1643 | } | 1647 | } |
1644 | 1648 | ||
1645 | 1649 | static void rtl8139_tx_timeout_task (void *_data) | |
1646 | static void rtl8139_tx_timeout (struct net_device *dev) | ||
1647 | { | 1650 | { |
1651 | struct net_device *dev = _data; | ||
1648 | struct rtl8139_private *tp = netdev_priv(dev); | 1652 | struct rtl8139_private *tp = netdev_priv(dev); |
1649 | void __iomem *ioaddr = tp->mmio_addr; | 1653 | void __iomem *ioaddr = tp->mmio_addr; |
1650 | int i; | 1654 | int i; |
1651 | u8 tmp8; | 1655 | u8 tmp8; |
1652 | unsigned long flags; | ||
1653 | 1656 | ||
1654 | printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x " | 1657 | printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x " |
1655 | "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd), | 1658 | "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd), |
@@ -1670,23 +1673,34 @@ static void rtl8139_tx_timeout (struct net_device *dev) | |||
1670 | if (tmp8 & CmdTxEnb) | 1673 | if (tmp8 & CmdTxEnb) |
1671 | RTL_W8 (ChipCmd, CmdRxEnb); | 1674 | RTL_W8 (ChipCmd, CmdRxEnb); |
1672 | 1675 | ||
1673 | spin_lock(&tp->rx_lock); | 1676 | spin_lock_bh(&tp->rx_lock); |
1674 | /* Disable interrupts by clearing the interrupt mask. */ | 1677 | /* Disable interrupts by clearing the interrupt mask. */ |
1675 | RTL_W16 (IntrMask, 0x0000); | 1678 | RTL_W16 (IntrMask, 0x0000); |
1676 | 1679 | ||
1677 | /* Stop a shared interrupt from scavenging while we are. */ | 1680 | /* Stop a shared interrupt from scavenging while we are. */ |
1678 | spin_lock_irqsave (&tp->lock, flags); | 1681 | spin_lock_irq(&tp->lock); |
1679 | rtl8139_tx_clear (tp); | 1682 | rtl8139_tx_clear (tp); |
1680 | spin_unlock_irqrestore (&tp->lock, flags); | 1683 | spin_unlock_irq(&tp->lock); |
1681 | 1684 | ||
1682 | /* ...and finally, reset everything */ | 1685 | /* ...and finally, reset everything */ |
1683 | if (netif_running(dev)) { | 1686 | if (netif_running(dev)) { |
1684 | rtl8139_hw_start (dev); | 1687 | rtl8139_hw_start (dev); |
1685 | netif_wake_queue (dev); | 1688 | netif_wake_queue (dev); |
1686 | } | 1689 | } |
1687 | spin_unlock(&tp->rx_lock); | 1690 | spin_unlock_bh(&tp->rx_lock); |
1688 | } | 1691 | } |
1689 | 1692 | ||
1693 | static void rtl8139_tx_timeout (struct net_device *dev) | ||
1694 | { | ||
1695 | struct rtl8139_private *tp = netdev_priv(dev); | ||
1696 | |||
1697 | if (!tp->have_thread) { | ||
1698 | INIT_WORK(&tp->thread, rtl8139_tx_timeout_task, dev); | ||
1699 | schedule_delayed_work(&tp->thread, next_tick); | ||
1700 | } else | ||
1701 | tp->watchdog_fired = 1; | ||
1702 | |||
1703 | } | ||
1690 | 1704 | ||
1691 | static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) | 1705 | static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) |
1692 | { | 1706 | { |