diff options
author | Russell King <rmk+kernel@armlinux.org.uk> | 2016-09-15 16:42:09 +0100 |
---|---|---|
committer | Russell King (Oracle) <rmk+kernel@armlinux.org.uk> | 2024-01-08 10:30:20 +0000 |
commit | 2b4c0434435ae0cfeeb5894d77fb74d09153357f (patch) | |
tree | ec1b207677879ef2fbb3dcae637dcbb3759eb1f6 | |
parent | 2c519f058fa0c3b766aa3d0171907c7d64ac6797 (diff) |
net: 3c589_cs: convert to NAPI
Convert the 3c589_cs driver to NAPI to ease the interrupt workload.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r-- | drivers/net/ethernet/3com/3c589_cs.c | 117 |
1 files changed, 88 insertions, 29 deletions
diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c index b9c7ec65aa84..75347796a903 100644 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ b/drivers/net/ethernet/3com/3c589_cs.c @@ -137,6 +137,8 @@ struct el3_private { u16 fast_poll; unsigned long last_irq; spinlock_t lock; + u16 pending; + struct napi_struct napi; }; static const char *if_names[] = { "auto", "10baseT", "10base2", "AUI" }; @@ -176,6 +178,7 @@ static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue); static void set_rx_mode(struct net_device *dev); static void set_multicast_list(struct net_device *dev); static const struct ethtool_ops netdev_ethtool_ops; +static int el3_napi_poll(struct napi_struct *napi, int budget); static void tc589_detach(struct pcmcia_device *p_dev); @@ -219,6 +222,8 @@ static int tc589_probe(struct pcmcia_device *link) dev->ethtool_ops = &netdev_ethtool_ops; + netif_napi_add(dev, &lp->napi, el3_napi_poll); + ret = tc589_config(link); if (ret) goto err_free_netdev; @@ -527,6 +532,8 @@ static int el3_open(struct net_device *dev) timer_setup(&lp->media, media_check, 0); mod_timer(&lp->media, jiffies + HZ); + napi_enable(&lp->napi); + dev_dbg(&link->dev, "%s: opened, status %4.4x.\n", dev->name, inw(dev->base_addr + EL3_STATUS)); @@ -547,10 +554,10 @@ static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue) netif_wake_queue(dev); } -static void pop_tx_status(struct net_device *dev) +static int pop_tx_status(struct net_device *dev) { unsigned int ioaddr = dev->base_addr; - int i; + int i, work = 0; /* Clear the Tx status stack. */ for (i = 32; i > 0; i--) { @@ -566,7 +573,9 @@ static void pop_tx_status(struct net_device *dev) dev->stats.tx_aborted_errors++; } outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ + work++; } + return work; } static netdev_tx_t el3_start_xmit(struct sk_buff *skb, @@ -606,47 +615,38 @@ static netdev_tx_t el3_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } -/* The EL3 interrupt handler. */ -static irqreturn_t el3_interrupt(int irq, void *dev_id) +static int el3_napi_handle(struct net_device *dev, int budget) { - struct net_device *dev = (struct net_device *) dev_id; struct el3_private *lp = netdev_priv(dev); - unsigned int ioaddr; + unsigned int ioaddr = dev->base_addr; __u16 status; - int i = 0, handled = 1; - - if (!netif_device_present(dev)) - return IRQ_NONE; - - ioaddr = dev->base_addr; - - netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS)); + int work = 0; - spin_lock(&lp->lock); - while ((status = inw(ioaddr + EL3_STATUS)) & + while ((status = (inw(ioaddr + EL3_STATUS) | lp->pending)) & (IntLatch | RxComplete | StatsFull)) { + lp->pending = 0; if ((status & 0xe000) != 0x2000) { netdev_dbg(dev, "interrupt from dead card\n"); - handled = 0; - break; + return 0; } if (status & RxComplete) - el3_rx(dev); + work += el3_rx(dev); if (status & TxAvailable) { netdev_dbg(dev, " TX room bit was handled.\n"); /* There's room in the FIFO for a full-sized packet. */ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); netif_wake_queue(dev); + work++; } if (status & TxComplete) - pop_tx_status(dev); + work += pop_tx_status(dev); if (status & (AdapterFailure | RxEarly | StatsFull)) { /* Handle all uncommon interrupts. */ if (status & StatsFull) /* Empty statistics. */ update_stats(dev); if (status & RxEarly) { /* Rx early is unused. */ - el3_rx(dev); + work += el3_rx(dev); outw(AckIntr | RxEarly, ioaddr + EL3_CMD); } if (status & AdapterFailure) { @@ -670,17 +670,74 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id) outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); } } - if (++i > 10) { - netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n", - status); - /* Clear all interrupts */ - outw(AckIntr | 0xFF, ioaddr + EL3_CMD); - break; - } /* Acknowledge the IRQ. */ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); } lp->last_irq = jiffies; + + return work; +} + +static int el3_napi_poll(struct napi_struct *napi, int budget) +{ + struct net_device *dev = napi->dev; + struct el3_private *lp = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + int work = 0; + + if (!netif_running(dev)) { + napi_complete(napi); + return 0; + } + + netdev_dbg(dev, "napi poll, status %4.4x.\n", inw(ioaddr + EL3_STATUS)); + + spin_lock(&lp->lock); + work = el3_napi_handle(dev, budget); + spin_unlock(&lp->lock); + + netdev_dbg(dev, "exiting napi poll, status %4.4x.\n", + inw(ioaddr + EL3_STATUS)); + + budget -= work; + if (budget > 0) { + napi_complete(napi); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete + | StatsFull | AdapterFailure, ioaddr + EL3_CMD); + } + + return work; +} + +/* The EL3 interrupt handler. */ +static irqreturn_t el3_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct el3_private *lp = netdev_priv(dev); + unsigned int ioaddr; + __u16 status; + int handled = 1; + + if (!netif_device_present(dev)) + return IRQ_NONE; + + ioaddr = dev->base_addr; + + netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS)); + + spin_lock(&lp->lock); + status = inw(ioaddr + EL3_STATUS); + if ((status & 0xe000) != 0x2000) { + netdev_dbg(dev, "interrupt from dead card\n"); + handled = 0; + } else { + lp->pending |= status; + outw(SetIntrEnb, ioaddr + EL3_CMD); + status &= IntLatch | TxAvailable | RxComplete + | StatsFull | AdapterFailure; + outw(AckIntr | status, ioaddr + EL3_CMD); + napi_schedule(&lp->napi); + } spin_unlock(&lp->lock); netdev_dbg(dev, "exiting interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS)); @@ -893,7 +950,7 @@ static int el3_rx(struct net_device *dev) } if (worklimit == 0) netdev_warn(dev, "too much work in el3_rx!\n"); - return 0; + return 32 - worklimit; } static void set_rx_mode(struct net_device *dev) @@ -953,6 +1010,8 @@ static int el3_close(struct net_device *dev) update_stats(dev); } + napi_disable(&lp->napi); + link->open--; netif_stop_queue(dev); del_timer_sync(&lp->media); |