summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2016-09-15 16:42:09 +0100
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2024-01-08 10:30:20 +0000
commit2b4c0434435ae0cfeeb5894d77fb74d09153357f (patch)
treeec1b207677879ef2fbb3dcae637dcbb3759eb1f6
parent2c519f058fa0c3b766aa3d0171907c7d64ac6797 (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.c117
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);