| From: | Stephen Hemminger <shemminger@osdl.org> | |
| To: | Jeff Garzik <jgarzik@pobox.com> | |
| Subject: | [RFT] NAPI for 8139too | |
| Date: | Fri, 17 Oct 2003 15:39:34 -0700 | |
| Cc: | netdev@oss.sgi.com |
Modified the 2.6.0-test7 driver to use NAPI. It reduces the CPU load
when getting excessive network traffic but doesn't change normal performance.
I get the same numbers with netperf for send and receive as before, but
when running a pktgen DOS style flood the CPU usage goes from 90+% down
to 75% and the system is still usable.
No problems found so far, but still needs more testing because
some of the error paths might break.
diff -Nru a/drivers/net/8139too.c b/drivers/net/8139too.c
--- a/drivers/net/8139too.c Fri Oct 17 15:32:35 2003
+++ b/drivers/net/8139too.c Fri Oct 17 15:32:35 2003
@@ -92,7 +92,7 @@
*/
#define DRV_NAME "8139too"
-#define DRV_VERSION "0.9.26"
+#define DRV_VERSION "0.9.27"
#include <linux/config.h>
@@ -159,9 +159,6 @@
static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
-
/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
The RTL chips use a 64 element hash table based on the Ethernet CRC. */
static int multicast_filter_limit = 32;
@@ -589,13 +586,11 @@
MODULE_LICENSE("GPL");
MODULE_PARM (multicast_filter_limit, "i");
-MODULE_PARM (max_interrupt_work, "i");
MODULE_PARM (media, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM (full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM (debug, "i");
MODULE_PARM_DESC (debug, "8139too bitmapped message enable number");
MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses");
-MODULE_PARM_DESC (max_interrupt_work, "8139too maximum events handled per interrupt");
MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps");
MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)");
@@ -618,6 +613,7 @@
static void __set_rx_mode (struct net_device *dev);
static void rtl8139_hw_start (struct net_device *dev);
static struct ethtool_ops rtl8139_ethtool_ops;
+static int rtl8139_poll(struct net_device *dev, int *budget);
#ifdef USE_IO_OPS
@@ -963,6 +959,8 @@
/* The Rtl8139-specific entries in the device structure. */
dev->open = rtl8139_open;
dev->hard_start_xmit = rtl8139_start_xmit;
+ dev->poll = rtl8139_poll;
+ dev->weight = 16;
dev->stop = rtl8139_close;
dev->get_stats = rtl8139_get_stats;
dev->set_multicast_list = rtl8139_set_rx_mode;
@@ -1879,24 +1877,28 @@
#endif
}
-static void rtl8139_rx_interrupt (struct net_device *dev,
- struct rtl8139_private *tp, void *ioaddr)
+/*
+ * NAPI poll routine.
+ */
+static int rtl8139_poll(struct net_device *dev, int *budget)
{
+ struct rtl8139_private *tp = dev->priv;
+ void *ioaddr = tp->mmio_addr;
unsigned char *rx_ring;
u16 cur_rx;
+ int rx, status;
+ unsigned long flags;
- assert (dev != NULL);
- assert (tp != NULL);
- assert (ioaddr != NULL);
-
+ spin_lock_irqsave(&tp->lock, flags);
+ rescan:
rx_ring = tp->rx_ring;
cur_rx = tp->cur_rx;
-
- DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+ rx = 0;
+ DPRINTK ("%s: In rtl8139_rx_poll(), current %4.4x BufAddr %4.4x,"
" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
RTL_R16 (RxBufAddr),
RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
-
+
while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
int ring_offset = cur_rx % RX_BUF_LEN;
u32 rx_status;
@@ -1943,18 +1945,13 @@
(rx_size < 8) ||
(!(rx_status & RxStatusOK))) {
rtl8139_rx_err (rx_status, dev, tp, ioaddr);
- return;
+ goto err;
}
- /* Malloc up new buffer, compatible with net-2e. */
- /* Omit the four octet CRC from the length. */
-
- /* TODO: consider allocating skb's outside of
- * interrupt context, both to speed interrupt processing,
- * and also to reduce the chances of having to
- * drop packets here under memory pressure.
- */
+ /* Drop lock so we can copy data with interrupts enabled. */
+ spin_unlock_irqrestore(&tp->lock, flags);
+ /* Omit the four octet CRC from the length. */
skb = dev_alloc_skb (pkt_size + 2);
if (skb) {
skb->dev = dev;
@@ -1964,22 +1961,35 @@
skb_put (skb, pkt_size);
skb->protocol = eth_type_trans (skb, dev);
- netif_rx (skb);
+ netif_receive_skb(skb);
dev->last_rx = jiffies;
tp->stats.rx_bytes += pkt_size;
tp->stats.rx_packets++;
} else {
- printk (KERN_WARNING
- "%s: Memory squeeze, dropping packet.\n",
- dev->name);
+ /* Since this board has a small fixed rx_ring,
+ * it is better to toss packets than hold them
+ * in the ring.
+ */
+ if (net_ratelimit())
+ printk (KERN_WARNING
+ "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
tp->stats.rx_dropped++;
}
+ spin_lock_irqsave(&tp->lock, flags);
cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
RTL_W16 (RxBufPtr, cur_rx - 16);
- if (RTL_R16 (IntrStatus) & RxAckBits)
+ status = RTL_R16 (IntrStatus) & RxAckBits;
+ if (status & (RxFIFOOver | RxOverflow))
+ tp->stats.rx_errors++;
+
+ if (status)
RTL_W16_F (IntrStatus, RxAckBits);
+
+ if (++rx >= dev->quota)
+ break;
}
DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
@@ -1988,6 +1998,23 @@
RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
tp->cur_rx = cur_rx;
+ err:
+ *budget -= rx;
+
+ if ((dev->quota -= rx) <= 0) {
+ spin_unlock_irqrestore(&tp->lock, flags);
+ return 1; /* not done */
+ }
+
+ /* last gasp check if interrupt still pending */
+ if (RTL_R16 (IntrStatus) & RxAckBits) {
+ DPRINTK("%s: going back for more work\n", dev->name);
+ goto rescan;
+ }
+ netif_rx_complete(dev);
+ RTL_W16 (IntrMask, rtl8139_intr_mask);
+ spin_unlock_irqrestore(&tp->lock, flags);
+ return 0; /* done */
}
@@ -2013,9 +2040,7 @@
status &= ~RxUnderrun;
}
- /* XXX along with rtl8139_rx_err, are we double-counting errors? */
- if (status &
- (RxUnderrun | RxOverflow | RxErr | RxFIFOOver))
+ if (status & RxUnderrun)
tp->stats.rx_errors++;
if (status & PCSTimeout)
@@ -2032,7 +2057,6 @@
}
}
-
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
@@ -2040,73 +2064,66 @@
{
struct net_device *dev = (struct net_device *) dev_instance;
struct rtl8139_private *tp = dev->priv;
- int boguscnt = max_interrupt_work;
void *ioaddr = tp->mmio_addr;
int ackstat, status;
int link_changed = 0; /* avoid bogus "uninit" warning */
- int handled = 0;
spin_lock (&tp->lock);
- do {
- status = RTL_R16 (IntrStatus);
-
- /* h/w no longer present (hotplug?) or major error, bail */
- if (status == 0xFFFF)
- break;
-
- if ((status &
- (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |
- RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0)
- break;
-
- handled = 1;
+ status = RTL_R16 (IntrStatus);
- /* Acknowledge all of the current interrupt sources ASAP, but
- an first get an additional status bit from CSCR. */
- if (status & RxUnderrun)
- link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
-
- /* The chip takes special action when we clear RxAckBits,
- * so we clear them later in rtl8139_rx_interrupt
- */
- ackstat = status & ~(RxAckBits | TxErr);
- RTL_W16 (IntrStatus, ackstat);
+ /* h/w no longer present (hotplug?) or major error, bail */
+ if (unlikely(status == 0xFFFF)) {
+ spin_unlock(&tp->lock);
+ return IRQ_HANDLED;
+ }
- DPRINTK ("%s: interrupt status=%#4.4x ackstat=%#4.4x new intstat=%#4.4x.\n",
- dev->name, status, ackstat, RTL_R16 (IntrStatus));
+ /* no interrupt source present */
+ if (unlikely((status & rtl8139_intr_mask) == 0)) {
+ spin_unlock(&tp->lock);
+ return IRQ_NONE;
+ }
- if (netif_running (dev) && (status & RxAckBits))
- rtl8139_rx_interrupt (dev, tp, ioaddr);
+ /* Acknowledge all of the current interrupt sources ASAP, but
+ an first get an additional status bit from CSCR. */
+ if (unlikely(status & RxUnderrun))
+ link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
- /* Check uncommon events with one test. */
- if (status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |
- RxFIFOOver | RxErr))
- rtl8139_weird_interrupt (dev, tp, ioaddr,
- status, link_changed);
-
- if (netif_running (dev) && (status & (TxOK | TxErr))) {
- rtl8139_tx_interrupt (dev, tp, ioaddr);
- if (status & TxErr)
- RTL_W16 (IntrStatus, TxErr);
- }
-
- boguscnt--;
- } while (boguscnt > 0);
+ /* The chip takes special action when we clear RxAckBits,
+ * so we clear them later in poll
+ */
+ ackstat = status & ~(RxAckBits | TxErr);
+ RTL_W16 (IntrStatus, ackstat);
- if (boguscnt <= 0) {
- printk (KERN_WARNING "%s: Too much work at interrupt, "
- "IntrStatus=0x%4.4x.\n", dev->name, status);
+ DPRINTK ("%s: interrupt status=%#4.4x ackstat=%#4.4x new intstat=%#4.4x.\n",
+ dev->name, status, ackstat, RTL_R16 (IntrStatus));
- /* Clear all interrupt sources. */
- RTL_W16 (IntrStatus, 0xffff);
+ /* If received interrupt, then disable furthur interrupts
+ * and enable NAPI polling.
+ */
+ if (netif_running (dev) && (status & RxAckBits)) {
+ /* disable more receive interrupts */
+ RTL_W16 (IntrMask, rtl8139_intr_mask & ~RxAckBits);
+ netif_rx_schedule(dev);
+ }
+
+ /* Check uncommon events with one test. */
+ if (unlikely(status & (PCIErr|PCSTimeout|RxUnderrun|RxOverflow))) {
+ rtl8139_weird_interrupt (dev, tp, ioaddr,
+ status, link_changed);
+ }
+
+ if (netif_running (dev) && (status & (TxOK | TxErr))) {
+ rtl8139_tx_interrupt (dev, tp, ioaddr);
+ if (status & TxErr)
+ RTL_W16 (IntrStatus, TxErr);
}
spin_unlock (&tp->lock);
DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
dev->name, RTL_R16 (IntrStatus));
- return IRQ_RETVAL(handled);
+ return IRQ_HANDLED;
}
Copyright © 2003, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds