Commit 70a8c581 authored by Jeff Garzik's avatar Jeff Garzik

[netdrvr tg3] fix NAPI deadlock

* do not hold driver spinlock during RX processing in tg3_poll
  (this is the deadlock fix)
* create netif_poll_{en,dis}able to synchronize against dev->poll()
* create __netif_rx_complete to avoid a third irq-save in tg3_poll
* create tg3_netif_{start,stop} as driver-specific helper functions
  which disable and enable NAPI polling and TX queueing.  Note that
  the TX queueing enable/disable is purely advisory, and is not
  intended to prevent any races.
* remove tg3_halt call from tg3_set_power_state, as all callers
  have already called tg3_halt, making it redundant.  Removing this
  function call also eliminates some locking complications.
* use new helper __netif_rx_complete in tg3_poll
* create tg3_reset_task, as a function that runs in process context
  which resets the NIC.  This is needed because tg3_netif_stop()
  calls schedule() in the process of disabling dev->poll.
* schedule tg3_reset_task from tg3_tx_timeout
* schedule tg3_reset_task from tg3_timer
* wrap several tg3_halt...tg3_init_hw sequences with
  tg3_netif_stop...tg3_netif_start.  In addition to synchronizing
  with dev->poll, this additionally fixes bugs where we were not
  calling netif_wake_queue, when we should have been.
* move netif_start_queue call to very bottom of tg3_open
* add missing tg3_netif_{start,stop} to tg3_{suspend,resume},
  further fixing obvious bugs.
parent 2b7eab5f
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/workqueue.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -228,6 +229,46 @@ static void tg3_enable_ints(struct tg3 *tp) ...@@ -228,6 +229,46 @@ static void tg3_enable_ints(struct tg3 *tp)
tp->grc_local_ctrl | GRC_LCLCTRL_SETINT); tp->grc_local_ctrl | GRC_LCLCTRL_SETINT);
} }
/* these three netif_xxx funcs should be moved into generic net layer */
static void netif_poll_disable(struct net_device *dev)
{
while (test_and_set_bit(__LINK_STATE_RX_SCHED, &dev->state)) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
}
}
static inline void netif_poll_enable(struct net_device *dev)
{
clear_bit(__LINK_STATE_RX_SCHED, &dev->state);
}
/* same as netif_rx_complete, except that local_irq_save(flags)
* has already been issued
*/
static inline void __netif_rx_complete(struct net_device *dev)
{
if (!test_bit(__LINK_STATE_RX_SCHED, &dev->state)) BUG();
list_del(&dev->poll_list);
clear_bit(__LINK_STATE_RX_SCHED, &dev->state);
}
static inline void tg3_netif_stop(struct tg3 *tp)
{
netif_stop_queue(tp->dev);
netif_poll_disable(tp->dev);
}
static inline void tg3_netif_start(struct tg3 *tp)
{
netif_poll_enable(tp->dev);
netif_wake_queue(tp->dev);
/* NOTE: unconditional netif_wake_queue is only appropriate
* so long as all callers are assured to have free tx slots
* (such as after tg3_init_hw)
*/
}
static void tg3_switch_clocks(struct tg3 *tp) static void tg3_switch_clocks(struct tg3 *tp)
{ {
if (tr32(TG3PCI_CLOCK_CTRL) & CLOCK_CTRL_44MHZ_CORE) { if (tr32(TG3PCI_CLOCK_CTRL) & CLOCK_CTRL_44MHZ_CORE) {
...@@ -387,7 +428,6 @@ static int tg3_phy_reset(struct tg3 *tp, int force) ...@@ -387,7 +428,6 @@ static int tg3_phy_reset(struct tg3 *tp, int force)
} }
static int tg3_setup_phy(struct tg3 *); static int tg3_setup_phy(struct tg3 *);
static int tg3_halt(struct tg3 *);
static int tg3_set_power_state(struct tg3 *tp, int state) static int tg3_set_power_state(struct tg3 *tp, int state)
{ {
...@@ -458,8 +498,6 @@ static int tg3_set_power_state(struct tg3 *tp, int state) ...@@ -458,8 +498,6 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
tg3_setup_phy(tp); tg3_setup_phy(tp);
} }
tg3_halt(tp);
pci_read_config_word(tp->pdev, pm + PCI_PM_PMC, &power_caps); pci_read_config_word(tp->pdev, pm + PCI_PM_PMC, &power_caps);
if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) { if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) {
...@@ -2044,7 +2082,12 @@ static int tg3_poll(struct net_device *netdev, int *budget) ...@@ -2044,7 +2082,12 @@ static int tg3_poll(struct net_device *netdev, int *budget)
spin_unlock(&tp->tx_lock); spin_unlock(&tp->tx_lock);
} }
/* run RX thread, within the bounds set by NAPI */ spin_unlock_irqrestore(&tp->lock, flags);
/* run RX thread, within the bounds set by NAPI.
* All RX "locking" is done by ensuring outside
* code synchronizes with dev->poll()
*/
done = 1; done = 1;
if (sblk->idx[0].rx_producer != tp->rx_rcb_ptr) { if (sblk->idx[0].rx_producer != tp->rx_rcb_ptr) {
int orig_budget = *budget; int orig_budget = *budget;
...@@ -2064,11 +2107,11 @@ static int tg3_poll(struct net_device *netdev, int *budget) ...@@ -2064,11 +2107,11 @@ static int tg3_poll(struct net_device *netdev, int *budget)
/* if no more work, tell net stack and NIC we're done */ /* if no more work, tell net stack and NIC we're done */
if (done) { if (done) {
netif_rx_complete(netdev); spin_lock_irqsave(&tp->lock, flags);
__netif_rx_complete(netdev);
tg3_enable_ints(tp); tg3_enable_ints(tp);
}
spin_unlock_irqrestore(&tp->lock, flags); spin_unlock_irqrestore(&tp->lock, flags);
}
return (done ? 0 : 1); return (done ? 0 : 1);
} }
...@@ -2136,17 +2179,21 @@ static void tg3_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -2136,17 +2179,21 @@ static void tg3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static void tg3_init_rings(struct tg3 *); static void tg3_init_rings(struct tg3 *);
static int tg3_init_hw(struct tg3 *); static int tg3_init_hw(struct tg3 *);
static int tg3_halt(struct tg3 *);
static void tg3_tx_timeout(struct net_device *dev) static void tg3_reset_task(void *_data)
{ {
struct tg3 *tp = dev->priv; struct tg3 *tp = _data;
unsigned int restart_timer;
printk(KERN_ERR PFX "%s: transmit timed out, resetting\n", tg3_netif_stop(tp);
dev->name);
spin_lock_irq(&tp->lock); spin_lock_irq(&tp->lock);
spin_lock(&tp->tx_lock); spin_lock(&tp->tx_lock);
restart_timer = tp->tg3_flags2 & TG3_FLG2_RESTART_TIMER;
tp->tg3_flags2 &= ~TG3_FLG2_RESTART_TIMER;
tg3_halt(tp); tg3_halt(tp);
tg3_init_rings(tp); tg3_init_rings(tp);
tg3_init_hw(tp); tg3_init_hw(tp);
...@@ -2154,7 +2201,20 @@ static void tg3_tx_timeout(struct net_device *dev) ...@@ -2154,7 +2201,20 @@ static void tg3_tx_timeout(struct net_device *dev)
spin_unlock(&tp->tx_lock); spin_unlock(&tp->tx_lock);
spin_unlock_irq(&tp->lock); spin_unlock_irq(&tp->lock);
netif_wake_queue(dev); tg3_netif_start(tp);
if (restart_timer)
mod_timer(&tp->timer, jiffies + 1);
}
static void tg3_tx_timeout(struct net_device *dev)
{
struct tg3 *tp = dev->priv;
printk(KERN_ERR PFX "%s: transmit timed out, resetting\n",
dev->name);
schedule_work(&tp->reset_task);
} }
#if !PCI_DMA_BUS_IS_PHYS #if !PCI_DMA_BUS_IS_PHYS
...@@ -2686,6 +2746,7 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu) ...@@ -2686,6 +2746,7 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu)
return 0; return 0;
} }
tg3_netif_stop(tp);
spin_lock_irq(&tp->lock); spin_lock_irq(&tp->lock);
spin_lock(&tp->tx_lock); spin_lock(&tp->tx_lock);
...@@ -2698,6 +2759,7 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu) ...@@ -2698,6 +2759,7 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu)
spin_unlock(&tp->tx_lock); spin_unlock(&tp->tx_lock);
spin_unlock_irq(&tp->lock); spin_unlock_irq(&tp->lock);
tg3_netif_start(tp);
return 0; return 0;
} }
...@@ -4408,9 +4470,11 @@ static void tg3_timer(unsigned long __opaque) ...@@ -4408,9 +4470,11 @@ static void tg3_timer(unsigned long __opaque)
} }
if (!(tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) { if (!(tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) {
tg3_halt(tp); tp->tg3_flags2 |= TG3_FLG2_RESTART_TIMER;
tg3_init_rings(tp); spin_unlock(&tp->tx_lock);
tg3_init_hw(tp); spin_unlock_irqrestore(&tp->lock, flags);
schedule_work(&tp->reset_task);
return;
} }
/* This part only runs once per second. */ /* This part only runs once per second. */
...@@ -4541,8 +4605,6 @@ static int tg3_open(struct net_device *dev) ...@@ -4541,8 +4605,6 @@ static int tg3_open(struct net_device *dev)
return err; return err;
} }
netif_start_queue(dev);
spin_lock_irq(&tp->lock); spin_lock_irq(&tp->lock);
spin_lock(&tp->tx_lock); spin_lock(&tp->tx_lock);
...@@ -4551,6 +4613,8 @@ static int tg3_open(struct net_device *dev) ...@@ -4551,6 +4613,8 @@ static int tg3_open(struct net_device *dev)
spin_unlock(&tp->tx_lock); spin_unlock(&tp->tx_lock);
spin_unlock_irq(&tp->lock); spin_unlock_irq(&tp->lock);
netif_start_queue(dev);
return 0; return 0;
} }
...@@ -5316,6 +5380,7 @@ static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr) ...@@ -5316,6 +5380,7 @@ static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr)
(ering.tx_pending > TG3_TX_RING_SIZE - 1)) (ering.tx_pending > TG3_TX_RING_SIZE - 1))
return -EINVAL; return -EINVAL;
tg3_netif_stop(tp);
spin_lock_irq(&tp->lock); spin_lock_irq(&tp->lock);
spin_lock(&tp->tx_lock); spin_lock(&tp->tx_lock);
...@@ -5329,6 +5394,7 @@ static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr) ...@@ -5329,6 +5394,7 @@ static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr)
netif_wake_queue(tp->dev); netif_wake_queue(tp->dev);
spin_unlock(&tp->tx_lock); spin_unlock(&tp->tx_lock);
spin_unlock_irq(&tp->lock); spin_unlock_irq(&tp->lock);
tg3_netif_start(tp);
return 0; return 0;
} }
...@@ -5351,6 +5417,7 @@ static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr) ...@@ -5351,6 +5417,7 @@ static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr)
if (copy_from_user(&epause, useraddr, sizeof(epause))) if (copy_from_user(&epause, useraddr, sizeof(epause)))
return -EFAULT; return -EFAULT;
tg3_netif_stop(tp);
spin_lock_irq(&tp->lock); spin_lock_irq(&tp->lock);
spin_lock(&tp->tx_lock); spin_lock(&tp->tx_lock);
if (epause.autoneg) if (epause.autoneg)
...@@ -5370,6 +5437,7 @@ static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr) ...@@ -5370,6 +5437,7 @@ static int tg3_ethtool_ioctl (struct net_device *dev, void *useraddr)
tg3_init_hw(tp); tg3_init_hw(tp);
spin_unlock(&tp->tx_lock); spin_unlock(&tp->tx_lock);
spin_unlock_irq(&tp->lock); spin_unlock_irq(&tp->lock);
tg3_netif_start(tp);
return 0; return 0;
} }
...@@ -6724,6 +6792,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, ...@@ -6724,6 +6792,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
spin_lock_init(&tp->lock); spin_lock_init(&tp->lock);
spin_lock_init(&tp->tx_lock); spin_lock_init(&tp->tx_lock);
spin_lock_init(&tp->indirect_lock); spin_lock_init(&tp->indirect_lock);
PREPARE_WORK(&tp->reset_task, tg3_reset_task, tp);
tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len); tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len);
if (tp->regs == 0UL) { if (tp->regs == 0UL) {
...@@ -6865,6 +6934,8 @@ static int tg3_suspend(struct pci_dev *pdev, u32 state) ...@@ -6865,6 +6934,8 @@ static int tg3_suspend(struct pci_dev *pdev, u32 state)
if (!netif_running(dev)) if (!netif_running(dev))
return 0; return 0;
tg3_netif_stop(tp);
spin_lock_irq(&tp->lock); spin_lock_irq(&tp->lock);
spin_lock(&tp->tx_lock); spin_lock(&tp->tx_lock);
tg3_disable_ints(tp); tg3_disable_ints(tp);
...@@ -6891,6 +6962,7 @@ static int tg3_suspend(struct pci_dev *pdev, u32 state) ...@@ -6891,6 +6962,7 @@ static int tg3_suspend(struct pci_dev *pdev, u32 state)
spin_unlock_irq(&tp->lock); spin_unlock_irq(&tp->lock);
netif_device_attach(dev); netif_device_attach(dev);
tg3_netif_start(tp);
} }
return err; return err;
...@@ -6921,6 +6993,8 @@ static int tg3_resume(struct pci_dev *pdev) ...@@ -6921,6 +6993,8 @@ static int tg3_resume(struct pci_dev *pdev)
spin_unlock(&tp->tx_lock); spin_unlock(&tp->tx_lock);
spin_unlock_irq(&tp->lock); spin_unlock_irq(&tp->lock);
tg3_netif_start(tp);
return 0; return 0;
} }
......
...@@ -1821,6 +1821,8 @@ struct tg3 { ...@@ -1821,6 +1821,8 @@ struct tg3 {
#define TG3_FLAG_GOT_SERDES_FLOWCTL 0x20000000 #define TG3_FLAG_GOT_SERDES_FLOWCTL 0x20000000
#define TG3_FLAG_SPLIT_MODE 0x40000000 #define TG3_FLAG_SPLIT_MODE 0x40000000
#define TG3_FLAG_INIT_COMPLETE 0x80000000 #define TG3_FLAG_INIT_COMPLETE 0x80000000
u32 tg3_flags2;
#define TG3_FLG2_RESTART_TIMER 0x00000001
u32 split_mode_max_reqs; u32 split_mode_max_reqs;
#define SPLIT_MODE_5704_MAX_REQ 3 #define SPLIT_MODE_5704_MAX_REQ 3
...@@ -1889,6 +1891,7 @@ struct tg3 { ...@@ -1889,6 +1891,7 @@ struct tg3 {
struct tg3_hw_stats *hw_stats; struct tg3_hw_stats *hw_stats;
dma_addr_t stats_mapping; dma_addr_t stats_mapping;
struct work_struct reset_task;
}; };
#endif /* !(_T3_H) */ #endif /* !(_T3_H) */
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