Re: [patch sungem] improved locking
From: Eric Lemoine <hidden>
Date: 2006-11-14 21:54:43
Subsystem:
networking drivers, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
On 11/14/06, David Miller [off-list ref] wrote:
From: "Eric Lemoine" <redacted> Date: Tue, 14 Nov 2006 08:28:42 +0100quoted
because it makes it explicit that only bits 0 through 6 are taken into account when writing the IACK register.The phrase "bits 0 through 6" doesn't make any sense when bit 3 DOES NOT EXIST in the hardware, it's reserved, it's not there, so including it only confuses people and obfuscates the code. Please use the explicit bit mask composed of existing macros, which not only makes sure that the mask has meaning, but it also makes sure that reserved and non-existing bits are never referenced.
Patch attached. Remember, the GEM_INTERRUPT_LOCKLESS isn't there to stay. It's currently there because I'm not sure the lockless implementation of gem_interrupt() will work with poll_net_controller. Thanks, Signed-off-by: Eric Lemoine <redacted>
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c
index 253e96e..31bd90d 100644
--- a/drivers/net/sungem.c
+++ b/drivers/net/sungem.c@@ -9,26 +9,6 @@ * * NAPI and NETPOLL support * (C) 2004 by Eric Lemoine (eric.lemoine@gmail.com) - * - * TODO: - * - Now that the driver was significantly simplified, I need to rework - * the locking. I'm sure we don't need _2_ spinlocks, and we probably - * can avoid taking most of them for so long period of time (and schedule - * instead). The main issues at this point are caused by the netdev layer - * though: - * - * gem_change_mtu() and gem_set_multicast() are called with a read_lock() - * help by net/core/dev.c, thus they can't schedule. That means they can't - * call netif_poll_disable() neither, thus force gem_poll() to
keep a spinlock - * where it could have been dropped. change_mtu especially would love also to - * be able to msleep instead of horrid locked delays when resetting the HW, - * but that read_lock() makes it impossible, unless I defer it's action to - * the reset task, which means it'll be asynchronous (won't take effect until - * the system schedules a bit). - * - * Also, it would probably be possible to also remove most of the long-life - * locking in open/resume code path (gem_reinit_chip) by beeing more careful - * about when we can start taking interrupts or get xmit() called... */ #include <linux/module.h>
@@ -206,11 +186,18 @@ static inline void phy_write(struct gem __phy_write(gp, gp->mii_phy_addr, reg, val); } -static inline void gem_enable_ints(struct gem *gp) +static inline void __gem_enable_ints(struct gem *gp) { - /* Enable all interrupts but TXDONE */ + /* Enable all interrupts, but TXDONE */ writel(GREG_STAT_TXDONE, gp->regs + GREG_IMASK); } + +static inline void gem_enable_ints(struct gem *gp) +{ + gp->irq_sync = 0; + wmb(); + __gem_enable_ints(gp); +} static inline void gem_disable_ints(struct gem *gp) {
@@ -218,6 +205,60 @@ static inline void gem_disable_ints(stru writel(GREG_STAT_NAPI | GREG_STAT_TXDONE, gp->regs + GREG_IMASK); } +#if GEM_INTERRUPT_LOCKLESS +static inline u32 gem_read_status2(struct gem *gp) +{ + return readl(gp->regs + GREG_STAT2); +} + +static inline void gem_ack_int(struct gem *gp) +{ + writel(GREG_STAT_IACK, gp->regs + GREG_IACK); +} + +#else +static inline u32 gem_read_status(struct gem *gp) +{ + return readl(gp->regs + GREG_STAT); +} +#endif + +static inline void gem_netif_stop(struct gem *gp) +{ + struct net_device *dev = gp->dev; + + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_poll_disable(dev); + netif_tx_disable(dev); +} + +static inline void gem_netif_start(struct gem *gp) +{ + struct net_device *dev = gp->dev; + + /* Unconditionnaly netif_wake_queue is ok so long as caller has + * freed tx slots, whis done in gem_reinit_chip(). + */ + netif_wake_queue(dev); + netif_poll_enable(dev); +} + +static inline void gem_schedule_reset_task(struct gem *gp) +{ + gp->reset_task_pending = 1; + smp_mb(); + schedule_work(&gp->reset_task); +} + +static inline void gem_wait_reset_task(struct gem *gp) +{ + mb(); + while (gp->reset_task_pending) { + yield(); + mb(); + } +} + static void gem_get_cell(struct gem *gp) { BUG_ON(gp->cell_enabled < 0);
@@ -658,12 +699,20 @@ static int gem_abnormal_irq(struct net_d return 0; do_reset: - gp->reset_task_pending = 1; - schedule_work(&gp->reset_task); + gem_schedule_reset_task(gp); return 1; } +static inline u32 gem_tx_avail(struct gem *gp) +{ + smp_mb(); + return (gp->tx_old <= gp->tx_new) ? + gp->tx_old + (TX_RING_SIZE - 1) - gp->tx_new : + gp->tx_old - gp->tx_new - 1; +} + + static __inline__ void gem_tx(struct net_device *dev, struct gem *gp,
u32 gem_status)
{
int entry, limit;@@ -717,11 +766,20 @@ static __inline__ void gem_tx(struct net gp->net_stats.tx_packets++; dev_kfree_skb_irq(skb); } + gp->tx_old = entry; - if (netif_queue_stopped(dev) && - TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1)) - netif_wake_queue(dev); + /* Need to make tx_old update visible to gem_start_xmit() */ + smp_mb(); + + if (unlikely(netif_queue_stopped(dev) && + gem_tx_avail(gp) > (MAX_SKB_FRAGS + 1))) { + netif_tx_lock(dev); + if (netif_queue_stopped(dev) && + gem_tx_avail(gp) > (MAX_SKB_FRAGS + 1)) + netif_wake_queue(dev); + netif_tx_unlock(dev); + } } static __inline__ void gem_post_rxds(struct gem *gp, int limit)
@@ -882,12 +940,6 @@ static int gem_rx(struct gem *gp, int wo static int gem_poll(struct net_device *dev, int *budget) { struct gem *gp = dev->priv; - unsigned long flags; - - /* - * NAPI locking nightmare: See comment at head of driver - */ - spin_lock_irqsave(&gp->lock, flags); do { int work_to_do, work_done;
@@ -899,19 +951,10 @@ static int gem_poll(struct net_device *d } /* Run TX completion thread */ - spin_lock(&gp->tx_lock); gem_tx(dev, gp, gp->status); - spin_unlock(&gp->tx_lock); - - spin_unlock_irqrestore(&gp->lock, flags); - /* Run RX thread. We don't use any locking here, - * code willing to do bad things - like cleaning the - * rx ring - must call netif_poll_disable(), which - * schedule_timeout()'s if polling is already disabled. - */ + /* Run RX thread */ work_to_do = min(*budget, dev->quota); - work_done = gem_rx(gp, work_to_do); *budget -= work_done;
@@ -920,23 +963,57 @@ static int gem_poll(struct net_device *d if (work_done >= work_to_do) return 1; - spin_lock_irqsave(&gp->lock, flags); - gp->status = readl(gp->regs + GREG_STAT); } while (gp->status & GREG_STAT_NAPI); __netif_rx_complete(dev); - gem_enable_ints(gp); + __gem_enable_ints(gp); - spin_unlock_irqrestore(&gp->lock, flags); return 0; } +static void gem_irq_quiesce(struct gem *gp) +{ + BUG_ON(gp->irq_sync); + + gp->irq_sync = 1; + smp_mb(); + + synchronize_irq(gp->pdev->irq); +} + +static inline int gem_irq_sync(struct gem *gp) +{ + return gp->irq_sync; +} + +static inline void gem_full_lock(struct gem *gp, int irq_sync) +{ + spin_lock_bh(&gp->lock); + if (irq_sync) + gem_irq_quiesce(gp); +} + +static inline void gem_full_unlock(struct gem *gp) +{ + spin_unlock_bh(&gp->lock); +} + +#if GEM_INTERRUPT_LOCKLESS + +/* Lockless implementation of IRQ handler. + * + * The downside of this implementation is that it requires three PIO + * operations: read status, disable interrupts and ack interrupt. + * + * Also, this implementation may not work with NET_POLL_CONTROLLER. + * TODO: need further investigation. + */ static irqreturn_t gem_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; struct gem *gp = dev->priv; - unsigned long flags; + u32 gem_status; /* Swallow interrupts when shutting the chip down, though * that shouldn't happen, we should have done free_irq() at
@@ -945,29 +1022,70 @@ static irqreturn_t gem_interrupt(int irq if (!gp->running) return IRQ_HANDLED; - spin_lock_irqsave(&gp->lock, flags); + gem_status = gem_read_status2(gp); + if (unlikely(!gem_status)) /* shared irq? */ + return IRQ_NONE; if (netif_rx_schedule_prep(dev)) { - u32 gem_status = readl(gp->regs + GREG_STAT); + gem_disable_ints(gp); + gem_ack_int(gp); - if (gem_status == 0) { + if (gem_irq_sync(gp)) { + /* We are sure that *we* disabled polling, so we can + * safely reenable it before returning. + */ netif_poll_enable(dev); - spin_unlock_irqrestore(&gp->lock, flags); - return IRQ_NONE; + return IRQ_HANDLED; } + gp->status = gem_status; - gem_disable_ints(gp); __netif_rx_schedule(dev); } - spin_unlock_irqrestore(&gp->lock, flags); + return IRQ_HANDLED; +} +#else +static irqreturn_t gem_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct gem *gp = dev->priv; + unsigned int handled = 1; + unsigned long flags; - /* If polling was disabled at the time we received that - * interrupt, we may return IRQ_HANDLED here while we - * should return IRQ_NONE. No big deal... + /* Swallow interrupts when shutting the chip down, though + * that shouldn't happen, we should have done free_irq() at + * this point... */ + if (!gp->running) + return IRQ_HANDLED; + + spin_lock_irqsave(&gp->int_lock, flags); + + if (netif_rx_schedule_prep(dev)) { + u32 gem_status = gem_read_status(gp); + + if (unlikely(!gem_status)) { + netif_poll_enable(dev); + handled = 0; + goto out; + } + + gem_disable_ints(gp); + + if (gem_irq_sync(gp)) { + netif_poll_enable(dev); + goto out; + } + + gp->status = gem_status; + __netif_rx_schedule(dev); + } + +out: + spin_unlock_irqrestore(&gp->int_lock, flags); return IRQ_HANDLED; } +#endif #ifdef CONFIG_NET_POLL_CONTROLLER static void gem_poll_controller(struct net_device *dev)
@@ -999,14 +1117,8 @@ static void gem_tx_timeout(struct net_de readl(gp->regs + MAC_RXSTAT), readl(gp->regs + MAC_RXCFG)); - spin_lock_irq(&gp->lock); - spin_lock(&gp->tx_lock); - - gp->reset_task_pending = 1; - schedule_work(&gp->reset_task); - - spin_unlock(&gp->tx_lock); - spin_unlock_irq(&gp->lock); + printk(KERN_INFO "%s: gem_tx_timeout\n", dev->name); + gem_schedule_reset_task(gp); } static __inline__ int gem_intme(int entry)
@@ -1023,7 +1135,6 @@ static int gem_start_xmit(struct sk_buff struct gem *gp = dev->priv; int entry; u64 ctrl; - unsigned long flags; ctrl = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) {
@@ -1037,24 +1148,13 @@ static int gem_start_xmit(struct sk_buff (csum_stuff_off << 21)); } - local_irq_save(flags); - if (!spin_trylock(&gp->tx_lock)) { - /* Tell upper layer to requeue */ - local_irq_restore(flags); - return NETDEV_TX_LOCKED; - } - /* We raced with gem_do_stop() */ - if (!gp->running) { - spin_unlock_irqrestore(&gp->tx_lock, flags); - return NETDEV_TX_BUSY; - } - /* This is a hard error, log it. */ - if (TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1)) { - netif_stop_queue(dev); - spin_unlock_irqrestore(&gp->tx_lock, flags); - printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", - dev->name); + if (gem_tx_avail(gp) <= (skb_shinfo(skb)->nr_frags + 1)) { + if (!netif_queue_stopped(dev)) { + netif_stop_queue(dev); + printk(KERN_ERR PFX "%s: BUG! Tx Ring full when " + "queue awake!\n", dev->name); + } return NETDEV_TX_BUSY; }
@@ -1131,15 +1231,17 @@ static int gem_start_xmit(struct sk_buff } gp->tx_new = entry; - if (TX_BUFFS_AVAIL(gp) <= (MAX_SKB_FRAGS + 1)) + if (unlikely(gem_tx_avail(gp) <= (MAX_SKB_FRAGS + 1))) { netif_stop_queue(dev); + if (gem_tx_avail(gp) > (MAX_SKB_FRAGS + 1)) + netif_wake_queue(dev); + } if (netif_msg_tx_queued(gp)) printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n", dev->name, entry, skb->len); mb(); writel(gp->tx_new, gp->regs + TXDMA_KICK); - spin_unlock_irqrestore(&gp->tx_lock, flags); dev->trans_start = jiffies;
@@ -1148,7 +1250,6 @@ static int gem_start_xmit(struct sk_buff #define STOP_TRIES 32 -/* Must be invoked under gp->lock and gp->tx_lock. */ static void gem_reset(struct gem *gp) { int limit;
@@ -1174,7 +1275,6 @@ static void gem_reset(struct gem *gp) printk(KERN_ERR "%s: SW reset is ghetto.\n", gp->dev->name); } -/* Must be invoked under gp->lock and gp->tx_lock. */ static void gem_start_dma(struct gem *gp) { u32 val;
@@ -1197,9 +1297,7 @@ static void gem_start_dma(struct gem *gp writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK); } -/* Must be invoked under gp->lock and gp->tx_lock. DMA won't be - * actually stopped before about 4ms tho ... - */ +/* DMA won't be actually stopped before about 4ms tho ... */ static void gem_stop_dma(struct gem *gp) { u32 val;
@@ -1220,8 +1318,7 @@ static void gem_stop_dma(struct gem *gp) } -/* Must be invoked under gp->lock and gp->tx_lock. */ -// XXX dbl check what that function should do when called on PCS PHY +/* XXX dbl check what that function should do when called on PCS PHY */ static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep) { u32 advertise, features;
@@ -1306,8 +1403,6 @@ non_mii: /* A link-up condition has occurred, initialize and enable the * rest of the chip. - * - * Must be invoked under gp->lock and gp->tx_lock. */ static int gem_set_link_modes(struct gem *gp) {
@@ -1417,7 +1512,6 @@ static int gem_set_link_modes(struct gem return 0; } -/* Must be invoked under gp->lock and gp->tx_lock. */ static int gem_mdio_link_not_up(struct gem *gp) { switch (gp->lstate) {
@@ -1474,13 +1568,13 @@ static void gem_link_timer(unsigned long if (gp->asleep) return; - spin_lock_irq(&gp->lock); - spin_lock(&gp->tx_lock); + gem_full_lock(gp, 0); gem_get_cell(gp); /* If the reset task is still pending, we just * reschedule the link timer */ + smp_mb(); if (gp->reset_task_pending) goto restart;
@@ -1528,8 +1622,7 @@ static void gem_link_timer(unsigned long printk(KERN_INFO "%s: Link down\n", gp->dev->name); netif_carrier_off(gp->dev); - gp->reset_task_pending = 1; - schedule_work(&gp->reset_task); + gem_schedule_reset_task(gp); restart_aneg = 1; } else if (++gp->timer_ticks > 10) { if (found_mii_phy(gp))
@@ -1546,11 +1639,9 @@ restart: mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10)); out_unlock: gem_put_cell(gp); - spin_unlock(&gp->tx_lock); - spin_unlock_irq(&gp->lock); + gem_full_unlock(gp); } -/* Must be invoked under gp->lock and gp->tx_lock. */ static void gem_clean_rings(struct gem *gp) { struct gem_init_block *gb = gp->init_block;
@@ -1601,7 +1692,6 @@ static void gem_clean_rings(struct gem * } } -/* Must be invoked under gp->lock and gp->tx_lock. */ static void gem_init_rings(struct gem *gp) { struct gem_init_block *gb = gp->init_block;
@@ -1775,12 +1865,9 @@ #endif netif_carrier_off(gp->dev); /* Can I advertise gigabit here ? I'd need BCM PHY docs... */ - spin_lock_irq(&gp->lock); gem_begin_auto_negotiation(gp, NULL); - spin_unlock_irq(&gp->lock); } -/* Must be invoked under gp->lock and gp->tx_lock. */ static void gem_init_dma(struct gem *gp) { u64 desc_dma = (u64) gp->gblock_dvma;
@@ -1818,7 +1905,6 @@ static void gem_init_dma(struct gem *gp) gp->regs + RXDMA_BLANK); } -/* Must be invoked under gp->lock and gp->tx_lock. */ static u32 gem_setup_multicast(struct gem *gp) { u32 rxcfg = 0;
@@ -1860,7 +1946,6 @@ static u32 gem_setup_multicast(struct ge return rxcfg; } -/* Must be invoked under gp->lock and gp->tx_lock. */ static void gem_init_mac(struct gem *gp) { unsigned char *e = &gp->dev->dev_addr[0];
@@ -1943,7 +2028,6 @@ #endif writel(0, gp->regs + WOL_WAKECSR); } -/* Must be invoked under gp->lock and gp->tx_lock. */ static void gem_init_pause_thresholds(struct gem *gp) { u32 cfg;
@@ -2096,7 +2180,6 @@ static int gem_check_invariants(struct g return 0; } -/* Must be invoked under gp->lock and gp->tx_lock. */ static void gem_reinit_chip(struct gem *gp) { /* Reset the chip */
@@ -2116,12 +2199,10 @@ static void gem_reinit_chip(struct gem * gem_init_mac(gp); } - /* Must be invoked with no lock held. */ static void gem_stop_phy(struct gem *gp, int wol) { u32 mifcfg; - unsigned long flags; /* Let the chip settle down a bit, it seems that helps * for sleep mode on some models
@@ -2167,13 +2248,13 @@ static void gem_stop_phy(struct gem *gp, writel(0, gp->regs + RXDMA_CFG); if (!wol) { - spin_lock_irqsave(&gp->lock, flags); - spin_lock(&gp->tx_lock); + gem_full_lock(gp, 0); + gem_reset(gp); writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST); writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST); - spin_unlock(&gp->tx_lock); - spin_unlock_irqrestore(&gp->lock, flags); + + gem_full_unlock(gp); /* No need to take the lock here */
@@ -2192,14 +2273,11 @@ static void gem_stop_phy(struct gem *gp, } } - static int gem_do_start(struct net_device *dev) { struct gem *gp = dev->priv; - unsigned long flags; - spin_lock_irqsave(&gp->lock, flags); - spin_lock(&gp->tx_lock); + gem_full_lock(gp, 0); /* Enable the cell */ gem_get_cell(gp);
@@ -2211,28 +2289,25 @@ static int gem_do_start(struct net_devic if (gp->lstate == link_up) { netif_carrier_on(gp->dev); + /* gem_set_link_modes starts DMA and enabled ints */ gem_set_link_modes(gp); } - netif_wake_queue(gp->dev); - - spin_unlock(&gp->tx_lock); - spin_unlock_irqrestore(&gp->lock, flags); + gem_full_unlock(gp); if (request_irq(gp->pdev->irq, gem_interrupt, - IRQF_SHARED, dev->name, (void *)dev)) { + IRQF_SHARED, dev->name, (void *)dev)) { + printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name); - spin_lock_irqsave(&gp->lock, flags); - spin_lock(&gp->tx_lock); + gem_full_lock(gp, 0); gp->running = 0; gem_reset(gp); gem_clean_rings(gp); gem_put_cell(gp); - spin_unlock(&gp->tx_lock); - spin_unlock_irqrestore(&gp->lock, flags); + gem_full_unlock(gp); return -EAGAIN; }
@@ -2243,22 +2318,14 @@ static int gem_do_start(struct net_devic static void gem_do_stop(struct net_device *dev, int wol) { struct gem *gp = dev->priv; - unsigned long flags; - - spin_lock_irqsave(&gp->lock, flags); - spin_lock(&gp->tx_lock); - - gp->running = 0; - - /* Stop netif queue */ - netif_stop_queue(dev); - /* Make sure ints are disabled */ + gem_full_lock(gp, 1); gem_disable_ints(gp); + + gp->running = 0; - /* We can drop the lock now */ - spin_unlock(&gp->tx_lock); - spin_unlock_irqrestore(&gp->lock, flags); + /* We can drop the lock now that running is set to 0 */ + gem_full_unlock(gp); /* If we are going to sleep with WOL */ gem_stop_dma(gp);
@@ -2275,9 +2342,9 @@ static void gem_do_stop(struct net_devic /* Cell not needed neither if no WOL */ if (!wol) { - spin_lock_irqsave(&gp->lock, flags); + gem_full_lock(gp, 0); gem_put_cell(gp); - spin_unlock_irqrestore(&gp->lock, flags); + gem_full_unlock(gp); } }
@@ -2287,30 +2354,26 @@ static void gem_reset_task(void *data) mutex_lock(&gp->pm_mutex); - netif_poll_disable(gp->dev); - - spin_lock_irq(&gp->lock); - spin_lock(&gp->tx_lock); + gem_full_lock(gp, 1); + gem_disable_ints(gp); if (gp->running == 0) goto not_running; - if (gp->running) { - netif_stop_queue(gp->dev); + gem_netif_stop(gp); - /* Reset the chip & rings */ - gem_reinit_chip(gp); - if (gp->lstate == link_up) - gem_set_link_modes(gp); - netif_wake_queue(gp->dev); - } - not_running: - gp->reset_task_pending = 0; + /* Reset the chip & rings */ + gem_reinit_chip(gp); + if (gp->lstate == link_up) + gem_set_link_modes(gp); - spin_unlock(&gp->tx_lock); - spin_unlock_irq(&gp->lock); + gem_netif_start(gp); - netif_poll_enable(gp->dev); +not_running: + gp->reset_task_pending = 0; + + gem_enable_ints(gp); + gem_full_unlock(gp); mutex_unlock(&gp->pm_mutex); }
@@ -2324,8 +2387,12 @@ static int gem_open(struct net_device *d mutex_lock(&gp->pm_mutex); /* We need the cell enabled */ - if (!gp->asleep) + if (!gp->asleep) { rc = gem_do_start(dev); + if (rc == 0) + netif_start_queue(dev); + } + gp->opened = (rc == 0); mutex_unlock(&gp->pm_mutex);
@@ -2337,15 +2404,14 @@ static int gem_close(struct net_device * { struct gem *gp = dev->priv; - /* Note: we don't need to call netif_poll_disable() here because - * our caller (dev_close) already did it for us - */ - mutex_lock(&gp->pm_mutex); gp->opened = 0; - if (!gp->asleep) + if (!gp->asleep) { + /* Upper layer took care of disabling polling */ + netif_stop_queue(dev); gem_do_stop(dev, 0); + } mutex_unlock(&gp->pm_mutex);
@@ -2357,22 +2423,17 @@ static int gem_suspend(struct pci_dev *p { struct net_device *dev = pci_get_drvdata(pdev); struct gem *gp = dev->priv; - unsigned long flags; mutex_lock(&gp->pm_mutex); - netif_poll_disable(dev); - printk(KERN_INFO "%s: suspending, WakeOnLan %s\n", dev->name, (gp->wake_on_lan && gp->opened) ? "enabled" : "disabled"); /* Keep the cell enabled during the entire operation */ - spin_lock_irqsave(&gp->lock, flags); - spin_lock(&gp->tx_lock); + gem_full_lock(gp, 0); gem_get_cell(gp); - spin_unlock(&gp->tx_lock); - spin_unlock_irqrestore(&gp->lock, flags); + gem_full_unlock(gp); /* If the driver is opened, we stop the MAC */ if (gp->opened) {
@@ -2381,6 +2442,7 @@ static int gem_suspend(struct pci_dev *p /* Switch off MAC, remember WOL setting */ gp->asleep_wol = gp->wake_on_lan; + gem_netif_stop(gp); gem_do_stop(dev, gp->asleep_wol); } else gp->asleep_wol = 0;
@@ -2399,8 +2461,7 @@ static int gem_suspend(struct pci_dev *p mutex_unlock(&gp->pm_mutex); /* Wait for a pending reset task to complete */ - while (gp->reset_task_pending) - yield(); + gem_wait_reset_task(gp); flush_scheduled_work(); /* Shut the PHY down eventually and setup WOL */
@@ -2421,7 +2482,6 @@ static int gem_resume(struct pci_dev *pd { struct net_device *dev = pci_get_drvdata(pdev); struct gem *gp = dev->priv; - unsigned long flags; printk(KERN_INFO "%s: resuming\n", dev->name);
@@ -2465,11 +2525,9 @@ static int gem_resume(struct pci_dev *pd /* Re-attach net device */ netif_device_attach(dev); - } - spin_lock_irqsave(&gp->lock, flags); - spin_lock(&gp->tx_lock); + gem_full_lock(gp, 0); /* If we had WOL enabled, the cell clock was never turned off during * sleep, so we end up beeing unbalanced. Fix that here
@@ -2482,10 +2540,9 @@ static int gem_resume(struct pci_dev *pd */ gem_put_cell(gp); - spin_unlock(&gp->tx_lock); - spin_unlock_irqrestore(&gp->lock, flags); + gem_full_unlock(gp); - netif_poll_enable(dev); + gem_netif_start(gp); mutex_unlock(&gp->pm_mutex);
@@ -2498,8 +2555,8 @@ static struct net_device_stats *gem_get_ struct gem *gp = dev->priv; struct net_device_stats *stats = &gp->net_stats; - spin_lock_irq(&gp->lock); - spin_lock(&gp->tx_lock); + gem_netif_stop(gp); + gem_full_lock(gp, 0); /* I have seen this being called while the PM was in progress, * so we shield against this
@@ -2522,27 +2579,26 @@ static struct net_device_stats *gem_get_ writel(0, gp->regs + MAC_LCOLL); } - spin_unlock(&gp->tx_lock); - spin_unlock_irq(&gp->lock); + gem_full_unlock(gp); + gem_netif_start(gp); return &gp->net_stats; } +/* gem_set_multicast is called with netif_tx_lock held. Thus, it + * cannot sleep. + */ static void gem_set_multicast(struct net_device *dev) { struct gem *gp = dev->priv; u32 rxcfg, rxcfg_new; int limit = 10000; - - spin_lock_irq(&gp->lock); - spin_lock(&gp->tx_lock); + gem_full_lock(gp, 0); if (!gp->running) goto bail; - netif_stop_queue(dev); - rxcfg = readl(gp->regs + MAC_RXCFG); rxcfg_new = gem_setup_multicast(gp); #ifdef STRIP_FCS
@@ -2562,11 +2618,8 @@ #endif writel(rxcfg, gp->regs + MAC_RXCFG); - netif_wake_queue(dev); - bail: - spin_unlock(&gp->tx_lock); - spin_unlock_irq(&gp->lock); + gem_full_unlock(gp); } /* Jumbo-grams don't seem to work :-( */
@@ -2593,16 +2646,22 @@ static int gem_change_mtu(struct net_dev } mutex_lock(&gp->pm_mutex); - spin_lock_irq(&gp->lock); - spin_lock(&gp->tx_lock); + + gem_netif_stop(gp); + gem_full_lock(gp, 1); + gem_disable_ints(gp); + dev->mtu = new_mtu; if (gp->running) { gem_reinit_chip(gp); if (gp->lstate == link_up) gem_set_link_modes(gp); } - spin_unlock(&gp->tx_lock); - spin_unlock_irq(&gp->lock); + + gem_netif_start(gp); + gem_enable_ints(gp); + gem_full_unlock(gp); + mutex_unlock(&gp->pm_mutex); return 0;
@@ -2635,7 +2694,7 @@ static int gem_get_settings(struct net_d cmd->phy_address = 0; /* XXX fixed PHYAD */ /* Return current PHY settings */ - spin_lock_irq(&gp->lock); + gem_full_lock(gp, 0); cmd->autoneg = gp->want_autoneg; cmd->speed = gp->phy_mii.speed; cmd->duplex = gp->phy_mii.duplex;
@@ -2647,7 +2706,7 @@ static int gem_get_settings(struct net_d */ if (cmd->advertising == 0) cmd->advertising = cmd->supported; - spin_unlock_irq(&gp->lock); + gem_full_unlock(gp); } else { // XXX PCS ? cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
@@ -2685,11 +2744,11 @@ static int gem_set_settings(struct net_d return -EINVAL; /* Apply settings and restart link process. */ - spin_lock_irq(&gp->lock); + gem_full_lock(gp, 0); gem_get_cell(gp); gem_begin_auto_negotiation(gp, cmd); gem_put_cell(gp); - spin_unlock_irq(&gp->lock); + gem_full_unlock(gp); return 0; }
@@ -2702,11 +2761,11 @@ static int gem_nway_reset(struct net_dev return -EINVAL; /* Restart link process. */ - spin_lock_irq(&gp->lock); + gem_full_lock(gp, 0); gem_get_cell(gp); gem_begin_auto_negotiation(gp, NULL); gem_put_cell(gp); - spin_unlock_irq(&gp->lock); + gem_full_unlock(gp); return 0; }
@@ -2770,16 +2829,15 @@ static int gem_ioctl(struct net_device * struct gem *gp = dev->priv; struct mii_ioctl_data *data = if_mii(ifr); int rc = -EOPNOTSUPP; - unsigned long flags; /* Hold the PM mutex while doing ioctl's or we may collide * with power management. */ mutex_lock(&gp->pm_mutex); - spin_lock_irqsave(&gp->lock, flags); + gem_full_lock(gp, 0); gem_get_cell(gp); - spin_unlock_irqrestore(&gp->lock, flags); + gem_full_unlock(gp); switch (cmd) { case SIOCGMIIPHY: /* Get address of MII PHY in use. */
@@ -2809,9 +2867,9 @@ static int gem_ioctl(struct net_device * break; }; - spin_lock_irqsave(&gp->lock, flags); + gem_full_lock(gp, 0); gem_put_cell(gp); - spin_unlock_irqrestore(&gp->lock, flags); + gem_full_unlock(gp); mutex_unlock(&gp->pm_mutex);
@@ -2918,6 +2976,7 @@ static void gem_remove_one(struct pci_de if (dev) { struct gem *gp = dev->priv; + /* unregister_netdev will close the device if it's open */ unregister_netdev(dev); /* Stop the link timer */
@@ -2927,8 +2986,7 @@ static void gem_remove_one(struct pci_de gem_get_cell(gp); /* Wait for a pending reset task to complete */ - while (gp->reset_task_pending) - yield(); + gem_wait_reset_task(gp); flush_scheduled_work(); /* Shut the PHY down */
@@ -3035,8 +3093,11 @@ static int __devinit gem_init_one(struct gp->msg_enable = DEFAULT_MSG; + gp->irq_sync = 0; spin_lock_init(&gp->lock); - spin_lock_init(&gp->tx_lock); +#if !GEM_INTERRUPT_LOCKLESS + spin_lock_init(&gp->int_lock); +#endif mutex_init(&gp->pm_mutex); init_timer(&gp->link_timer);
@@ -3132,9 +3193,7 @@ #endif */ gem_init_phy(gp); - spin_lock_irq(&gp->lock); gem_put_cell(gp); - spin_unlock_irq(&gp->lock); /* Register with kernel */ if (register_netdev(dev)) {
@@ -3156,8 +3215,7 @@ #endif printk(KERN_INFO "%s: Found %s PHY\n", dev->name, gp->phy_mii.def ? gp->phy_mii.def->name : "no"); - /* GEM can do it all... */ - dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_LLTX; + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; if (pci_using_dac) dev->features |= NETIF_F_HIGHDMA;
@@ -3177,7 +3235,6 @@ err_out_free_netdev: err_disable_device: pci_disable_device(pdev); return err; - }
diff --git a/drivers/net/sungem.h b/drivers/net/sungem.h
index a70067c..49d3aa5 100644
--- a/drivers/net/sungem.h
+++ b/drivers/net/sungem.h@@ -7,6 +7,8 @@ #ifndef _SUNGEM_H #define _SUNGEM_H +#define GEM_INTERRUPT_LOCKLESS 1 /* TODO */ + /* Global Registers */ #define GREG_SEBSTATE 0x0000UL /* SEB State Register */ #define GREG_CFG 0x0004UL /* Configuration Register */
@@ -71,6 +73,11 @@ #define GREG_STAT_NAPI (GREG_STAT_TXALL * on GREG_STAT. */ +#if GEM_INTERRUPT_LOCKLESS +#define GREG_STAT_IACK (GREG_STAT_TXALL | GREG_STAT_TXINTME | \ + GREG_STAT_RXDONE | GREG_STAT_RXNOBUF) +#endif + /* Global PCI Error Status Register */ #define GREG_PCIESTAT_BADACK 0x00000001 /* No ACK64# during ABS64 cycle */ #define GREG_PCIESTAT_DTRTO 0x00000002 /* Delayed transaction timeout */
@@ -973,8 +980,11 @@ enum link_state { }; struct gem { + int irq_sync; spinlock_t lock; - spinlock_t tx_lock; +#if !GEM_INTERRUPT_LOCKLESS + spinlock_t int_lock; +#endif void __iomem *regs; int rx_new, rx_old; int tx_new, tx_old; --
Eric
Attachments
- sungem-locking.patch [text/x-patch] 28163 bytes · preview