[PATCH] pcnet32 driver NAPI support

From: Lennart Sorensen
Date: Wed Jun 07 2006 - 12:51:11 EST


I have added NAPI support to the pcnet32 driver. This has greatly
improved the responsiveness on my systems (geode GX1 266MHz) when under
heavy network load. Without this change the system would become
unresponsive due to interrupts when flooded with traffic, and eventually
the watchdog would reboot the system due to the watchdog daemon being
starved for cpu time. With the patch the system is still useable on a
serial console, although very slow. Network throughput is also higher
since more time is spend processing packets and getting them sent out,
instead of only spending time acknowledging interrupts from incoming
packets.

Now having never actually done a patch submission to the kernel before,
I will try and see if I can do it right.

The patch adds a PCNET32_NAPI config option to drivers/net/Kconfig, and
the appropriate code to support the option to drivers/net/pcnet32.c and
has been tested on many of my systems (allthough they are allmost all
identical, and require some extra patches to pcnet32 due to not having
an EEPROM installed), and on an AT-2700TX.

I have made a diff against 2.6.16.20 and 2.6.17-rc6.

Comments would be very welcome.

Signed-off-by: Len Sorensen <lsorense@xxxxxxxxxxxxxxxxxxx>

Len Sorensen
diff -ruN linux-2.6.16.20/drivers/net/Kconfig linux-2.6.16.20.pcnet32napi/drivers/net/Kconfig
--- linux-2.6.16.20/drivers/net/Kconfig 2006-06-05 13:18:23.000000000 -0400
+++ linux-2.6.16.20.pcnet32napi/drivers/net/Kconfig 2006-06-07 11:19:54.000000000 -0400
@@ -1272,6 +1272,23 @@
<file:Documentation/networking/net-modules.txt>. The module
will be called pcnet32.

+config PCNET32_NAPI
+ bool "Use NAPI RX polling "
+ depends on PCNET32
+ help
+ NAPI is a new driver API designed to reduce CPU and interrupt load
+ when the driver is receiving lots of packets from the card. It is
+ still somewhat experimental and thus not yet enabled by default.
+
+ If your estimated Rx load is 10kpps or more, or if the card will be
+ deployed on potentially unfriendly networks (e.g. in a firewall),
+ then say Y here.
+
+ See <file:Documentation/networking/NAPI_HOWTO.txt> for more
+ information.
+
+ If in doubt, say N.
+
config AMD8111_ETH
tristate "AMD 8111 (new PCI lance) support"
depends on NET_PCI && PCI
diff -ruN linux-2.6.16.20/drivers/net/pcnet32.c linux-2.6.16.20.pcnet32napi/drivers/net/pcnet32.c
--- linux-2.6.16.20/drivers/net/pcnet32.c 2006-06-05 13:18:23.000000000 -0400
+++ linux-2.6.16.20.pcnet32napi/drivers/net/pcnet32.c 2006-06-07 12:00:36.000000000 -0400
@@ -21,9 +21,15 @@
*
*************************************************************************/

+#include <linux/config.h>
+
+#ifdef CONFIG_PCNET32_NAPI
+#define DRV_NAME "pcnet32napi"
+#else
#define DRV_NAME "pcnet32"
-#define DRV_VERSION "1.31c"
-#define DRV_RELDATE "01.Nov.2005"
+#endif
+#define DRV_VERSION "1.31d"
+#define DRV_RELDATE "06.Jun.2006"
#define PFX DRV_NAME ": "

static const char *version =
@@ -265,6 +271,7 @@
* v1.31c 01 Nov 2005 Don Fry Allied Telesyn 2700/2701 FX are 100Mbit only.
* Force 100Mbit FD if Auto (ASEL) is selected.
* See Bugzilla 2669 and 4551.
+ * v1.31d 06 Jun 2006 Len Sorensen added NAPI support.
*/


@@ -383,6 +390,7 @@
struct mii_if_info mii_if;
struct timer_list watchdog_timer;
struct timer_list blink_timer;
+ struct timer_list oom_timer;
u32 msg_enable; /* debug message level */
};

@@ -392,7 +400,13 @@
static int pcnet32_open(struct net_device *);
static int pcnet32_init_ring(struct net_device *);
static int pcnet32_start_xmit(struct sk_buff *, struct net_device *);
+#ifdef CONFIG_PCNET32_NAPI
+void disable_rx_and_norxbuff_ints(struct net_device *dev);
+void enable_rx_and_norxbuff_ints(struct net_device *dev);
+static int pcnet32_poll(struct net_device *, int *budget);
+#else
static int pcnet32_rx(struct net_device *);
+#endif
static void pcnet32_tx_timeout (struct net_device *dev);
static irqreturn_t pcnet32_interrupt(int, void *, struct pt_regs *);
static int pcnet32_close(struct net_device *);
@@ -422,6 +436,174 @@
PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
};

+#ifdef CONFIG_PCNET32_NAPI
+void oom_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct pcnet32_private *lp = dev->priv;
+ lp->rl_active = 0;
+ netif_rx_schedule(dev);
+}
+
+static
+int pcnet32_poll(struct net_device *dev, int *budget)
+{
+ struct pcnet32_private *lp = dev->priv;
+ ulong ioaddr = dev->base_addr;
+
+ int entry = lp->cur_rx & lp->rx_mod_mask;
+ int rx_work_limit = dev->quota;
+ int received = 0;
+
+ if (!netif_running(dev))
+ goto done;
+
+ do {
+ // Clear RX interrupts
+ lp->a.write_csr (ioaddr, 0, 0x1400);
+
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {
+ int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8;
+
+ if (status != 0x03) { /* There was an error. */
+ /*
+ * There is a tricky error noted by John Murphy,
+ * <murf@xxxxxxxxxxxx> to Russ Nelson: Even with full-sized
+ * buffers it's possible for a jabber packet to use two
+ * buffers, with only the last correctly noting the error.
+ */
+ if (status & 0x01) /* Only count a general error at the */
+ lp->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x20) lp->stats.rx_frame_errors++;
+ if (status & 0x10) lp->stats.rx_over_errors++;
+ if (status & 0x08) lp->stats.rx_crc_errors++;
+ if (status & 0x04) lp->stats.rx_fifo_errors++;
+ lp->rx_ring[entry].status &= le16_to_cpu(0x03ff);
+ } else {
+ /* Malloc up new buffer, compatible with net-2e. */
+ short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4;
+ struct sk_buff *skb;
+
+ /* Discard oversize frames. */
+ if (unlikely(pkt_len > PKT_BUF_SZ - 2)) {
+ if (netif_msg_drv(lp))
+ printk(KERN_ERR "%s: Impossible packet size %d!\n",
+ dev->name, pkt_len);
+ lp->stats.rx_errors++;
+ } else if (pkt_len < 60) {
+ if (netif_msg_rx_err(lp))
+ printk(KERN_ERR "%s: Runt packet!\n", dev->name);
+ lp->stats.rx_errors++;
+ } else {
+ int rx_in_place = 0;
+ if (--rx_work_limit < 0) {
+ goto not_done;
+ }
+ if (pkt_len > rx_copybreak) {
+ struct sk_buff *newskb;
+ if ((newskb = dev_alloc_skb(PKT_BUF_SZ))) {
+ skb_reserve (newskb, 2);
+ skb = lp->rx_skbuff[entry];
+ pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[entry],
+ PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE);
+ skb_put (skb, pkt_len);
+ lp->rx_skbuff[entry] = newskb;
+ newskb->dev = dev;
+ lp->rx_dma_addr[entry] =
+ pci_map_single(lp->pci_dev, newskb->data,
+ PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE);
+ lp->rx_ring[entry].base = le32_to_cpu(lp->rx_dma_addr[entry]);
+ rx_in_place = 1;
+ } else
+ skb = NULL;
+ } else {
+ skb = dev_alloc_skb(pkt_len+2);
+ }
+ if (skb == NULL) {
+ int i;
+ if (netif_msg_drv(lp))
+ printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n",
+ dev->name);
+ for (i = 0; i < lp->rx_ring_size; i++)
+ if ((short)le16_to_cpu(lp->rx_ring[(entry+i)
+ & lp->rx_mod_mask].status) < 0)
+ break;
+
+ if (i > lp->rx_ring_size -2) {
+ lp->stats.rx_dropped++;
+ lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+ wmb(); /* Make sure adapter sees owner change */
+ lp->cur_rx++;
+ }
+ goto oom;
+ }
+ skb->dev = dev;
+ if (!rx_in_place) {
+ skb_reserve(skb,2); /* 16 byte align */
+ skb_put(skb,pkt_len); /* Make room */
+ pci_dma_sync_single_for_cpu(lp->pci_dev,
+ lp->rx_dma_addr[entry],
+ PKT_BUF_SZ-2,
+ PCI_DMA_FROMDEVICE);
+ eth_copy_and_sum(skb,
+ (unsigned char *)(lp->rx_skbuff[entry]->data),
+ pkt_len,0);
+ pci_dma_sync_single_for_device(lp->pci_dev,
+ lp->rx_dma_addr[entry],
+ PKT_BUF_SZ-2,
+ PCI_DMA_FROMDEVICE);
+ }
+ lp->stats.rx_bytes += skb->len;
+ skb->protocol=eth_type_trans(skb,dev);
+ dev->last_rx = jiffies;
+ netif_receive_skb(skb);
+ lp->stats.rx_packets++;
+ }
+ }
+ /*
+ * The docs say that the buffer length isn't touched, but Andrew Boyd
+ * of QNX reports that some revs of the 79C965 clear it.
+ */
+ lp->rx_ring[entry].buf_length = le16_to_cpu(2-PKT_BUF_SZ);
+ wmb(); /* Make sure owner changes after all others are visible */
+ lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+ entry = (++lp->cur_rx) & lp->rx_mod_mask;
+ received++;
+ }
+ } while(lp->a.read_csr (ioaddr, 0) & 0x1400);
+
+done:
+
+ dev->quota -= received;
+ *budget -= received;
+
+ /* Remove us from polling list and enable RX intr. */
+ netif_rx_complete(dev);
+ enable_rx_and_norxbuff_ints(dev);
+
+ return 0;
+
+not_done:
+ if (!received) {
+ received = dev->quota; /* Not to happen */
+ }
+ dev->quota -= received;
+ *budget -= received;
+
+ return 1;
+
+oom: /* Executed with RX ints disabled */
+
+ /* Start timer, stop polling, but do not enable rx interrupts. */
+ mod_timer(&lp->oom_timer, jiffies+1);
+
+ /* remove ourselves from the polling list */
+ netif_rx_complete(dev);
+
+ return 0;
+}
+#endif

static u16 pcnet32_wio_read_csr (unsigned long addr, int index)
{
@@ -775,7 +957,7 @@
lp->tx_ring[x].length = le16_to_cpu(-skb->len);
lp->tx_ring[x].misc = 0;

- /* put DA and SA into the skb */
+ /* put DA and SA into the skb */
for (i=0; i<6; i++)
*packet++ = dev->dev_addr[i];
for (i=0; i<6; i++)
@@ -1334,6 +1516,12 @@
lp->mii_if.mdio_read = mdio_read;
lp->mii_if.mdio_write = mdio_write;

+#ifdef CONFIG_PCNET32_NAPI
+ init_timer(&lp->oom_timer);
+ lp->oom_timer.data = (unsigned long)dev;
+ lp->oom_timer.function = oom_timer;
+#endif
+
if (fdx && !(lp->options & PCNET32_PORT_ASEL) &&
((cards_found>=MAX_UNITS) || full_duplex[cards_found]))
lp->options |= PCNET32_PORT_FD;
@@ -1418,6 +1606,10 @@
dev->ethtool_ops = &pcnet32_ethtool_ops;
dev->tx_timeout = pcnet32_tx_timeout;
dev->watchdog_timeo = (5*HZ);
+#ifdef CONFIG_PCNET32_NAPI
+ dev->poll = pcnet32_poll;
+ dev->weight = 16;
+#endif

#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = pcnet32_poll_controller;
@@ -1949,6 +2141,38 @@
return 0;
}

+void
+disable_rx_and_norxbuff_ints(struct net_device *dev)
+{
+ u16 csr3,rap;
+ unsigned long ioaddr;
+ struct pcnet32_private *lp;
+
+ ioaddr = dev->base_addr;
+ lp = dev->priv;
+ rap = lp->a.read_rap(ioaddr);
+ csr3 = lp->a.read_csr (ioaddr, 3);
+ lp->a.write_csr (ioaddr, 3, csr3 | 0x1400); // Set the MISSM and RINTM bits
+ lp->a.write_rap (ioaddr,rap);
+}
+
+void
+enable_rx_and_norxbuff_ints(struct net_device *dev)
+{
+ u16 csr3,rap;
+ unsigned long ioaddr;
+ struct pcnet32_private *lp;
+
+ ioaddr = dev->base_addr;
+ lp = dev->priv;
+ rap = lp->a.read_rap(ioaddr);
+ csr3 = lp->a.read_csr (ioaddr, 3);
+ lp->a.write_csr (ioaddr, 3, csr3 & ~0x1400); // Clear the MISSM and RINTM bits
+ /* Set interrupt enable. */
+ lp->a.write_csr (ioaddr, 0, 0x0040);
+ lp->a.write_rap (ioaddr,rap);
+}
+
/* The PCNET32 interrupt handler. */
static irqreturn_t
pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
@@ -1978,7 +2202,11 @@
break; /* PCMCIA remove happened */
}
/* Acknowledge all of the current interrupt sources ASAP. */
+#ifdef CONFIG_PCNET32_NAPI
+ lp->a.write_csr (ioaddr, 0, csr0 & ~0x144f);
+#else
lp->a.write_csr (ioaddr, 0, csr0 & ~0x004f);
+#endif

must_restart = 0;

@@ -1986,8 +2214,18 @@
printk(KERN_DEBUG "%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
dev->name, csr0, lp->a.read_csr (ioaddr, 0));

- if (csr0 & 0x0400) /* Rx interrupt */
+ if (csr0 & 0x0400) { /* Rx interrupt */
+#ifdef CONFIG_PCNET32_NAPI
+ if(!lp->rl_active) {
+ disable_rx_and_norxbuff_ints(dev);
+ netif_rx_schedule(dev);
+ } else {
+ lp->a.write_csr (ioaddr, 0, 0x0400);
+ }
+#else
pcnet32_rx(dev);
+#endif
+ }

if (csr0 & 0x0200) { /* Tx-done interrupt */
unsigned int dirty_tx = lp->dirty_tx;
@@ -2084,7 +2322,16 @@
* interrupt, but a missed frame interrupt sooner or later.
* So we try to clean up our receive ring here.
*/
+#ifdef CONFIG_PCNET32_NAPI
+ if(!lp->rl_active) {
+ disable_rx_and_norxbuff_ints(dev);
+ netif_rx_schedule(dev);
+ } else {
+ lp->a.write_csr (ioaddr, 0, 0x1000);
+ }
+#else
pcnet32_rx(dev);
+#endif
lp->stats.rx_errors++; /* Missed a Rx frame. */
}
if (csr0 & 0x0800) {
@@ -2116,6 +2363,7 @@
return IRQ_HANDLED;
}

+#ifndef CONFIG_PCNET32_NAPI
static int
pcnet32_rx(struct net_device *dev)
{
@@ -2235,6 +2483,7 @@

return 0;
}
+#endif

static int
pcnet32_close(struct net_device *dev)
@@ -2245,6 +2494,9 @@
unsigned long flags;

del_timer_sync(&lp->watchdog_timer);
+#ifdef CONFIG_PCNET32_NAPI
+ del_timer_sync(&lp->oom_timer);
+#endif

netif_stop_queue(dev);

diff -ruN linux-2.6.17-rc6/drivers/net/Kconfig linux-2.6.17-rc6.pcnet32napi/drivers/net/Kconfig
--- linux-2.6.17-rc6/drivers/net/Kconfig 2006-06-05 13:18:23.000000000 -0400
+++ linux-2.6.17-rc6.pcnet32napi/drivers/net/Kconfig 2006-06-07 11:19:54.000000000 -0400
@@ -1272,6 +1272,23 @@
<file:Documentation/networking/net-modules.txt>. The module
will be called pcnet32.

+config PCNET32_NAPI
+ bool "Use NAPI RX polling "
+ depends on PCNET32
+ help
+ NAPI is a new driver API designed to reduce CPU and interrupt load
+ when the driver is receiving lots of packets from the card. It is
+ still somewhat experimental and thus not yet enabled by default.
+
+ If your estimated Rx load is 10kpps or more, or if the card will be
+ deployed on potentially unfriendly networks (e.g. in a firewall),
+ then say Y here.
+
+ See <file:Documentation/networking/NAPI_HOWTO.txt> for more
+ information.
+
+ If in doubt, say N.
+
config AMD8111_ETH
tristate "AMD 8111 (new PCI lance) support"
depends on NET_PCI && PCI
diff -ruN linux-2.6.17-rc6/drivers/net/pcnet32.c linux-2.6.17-rc6.pcnet32napi/drivers/net/pcnet32.c
--- linux-2.6.17-rc6/drivers/net/pcnet32.c 2006-06-05 13:18:23.000000000 -0400
+++ linux-2.6.17-rc6.pcnet32napi/drivers/net/pcnet32.c 2006-06-07 12:00:36.000000000 -0400
@@ -21,9 +21,15 @@
*
*************************************************************************/

+#include <linux/config.h>
+
+#ifdef CONFIG_PCNET32_NAPI
+#define DRV_NAME "pcnet32napi"
+#else
#define DRV_NAME "pcnet32"
-#define DRV_VERSION "1.32"
-#define DRV_RELDATE "18.Mar.2006"
+#endif
+#define DRV_VERSION "1.32a"
+#define DRV_RELDATE "06.Jun.2006"
#define PFX DRV_NAME ": "

static const char *const version =
@@ -271,6 +277,7 @@
struct mii_if_info mii_if;
struct timer_list watchdog_timer;
struct timer_list blink_timer;
+ struct timer_list oom_timer;
u32 msg_enable; /* debug message level */

/* each bit indicates an available PHY */
@@ -283,7 +290,13 @@
static int pcnet32_open(struct net_device *);
static int pcnet32_init_ring(struct net_device *);
static int pcnet32_start_xmit(struct sk_buff *, struct net_device *);
+#ifdef CONFIG_PCNET32_NAPI
+void disable_rx_and_norxbuff_ints(struct net_device *dev);
+void enable_rx_and_norxbuff_ints(struct net_device *dev);
+static int pcnet32_poll(struct net_device *, int *budget);
+#else
static int pcnet32_rx(struct net_device *);
+#endif
static void pcnet32_tx_timeout(struct net_device *dev);
static irqreturn_t pcnet32_interrupt(int, void *, struct pt_regs *);
static int pcnet32_close(struct net_device *);
@@ -315,6 +328,213 @@
0x10 << 2, PCI_ADDR3 = 0x10 << 3,
};

+#ifdef CONFIG_PCNET32_NAPI
+void oom_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct pcnet32_private *lp = dev->priv;
+ lp->rl_active = 0;
+ netif_rx_schedule(dev);
+}
+
+static
+int pcnet32_poll(struct net_device *dev, int *budget)
+{
+ struct pcnet32_private *lp = dev->priv;
+ ulong ioaddr = dev->base_addr;
+
+ int entry = lp->cur_rx & lp->rx_mod_mask;
+ int rx_work_limit = dev->quota;
+ int received = 0;
+
+ if (!netif_running(dev))
+ goto done;
+
+ do {
+ // Clear RX interrupts
+ lp->a.write_csr (ioaddr, 0, 0x1400);
+
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {
+ int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8;
+
+ if (status != 0x03) { /* There was an error. */
+ /*
+ * There is a tricky error noted by John Murphy,
+ * <murf@xxxxxxxxxxxx> to Russ Nelson: Even with full-sized
+ * buffers it's possible for a jabber packet to use two
+ * buffers, with only the last correctly noting the error.
+ */
+ if (status & 0x01) /* Only count a general error at the */
+ lp->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x20)
+ lp->stats.rx_frame_errors++;
+ if (status & 0x10)
+ lp->stats.rx_over_errors++;
+ if (status & 0x08)
+ lp->stats.rx_crc_errors++;
+ if (status & 0x04)
+ lp->stats.rx_fifo_errors++;
+ lp->rx_ring[entry].status &= le16_to_cpu(0x03ff);
+ } else {
+ /* Malloc up new buffer, compatible with net-2e. */
+ short pkt_len =
+ (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)
+ - 4;
+ struct sk_buff *skb;
+
+ /* Discard oversize frames. */
+ if (unlikely(pkt_len > PKT_BUF_SZ - 2)) {
+ if (netif_msg_drv(lp))
+ printk(KERN_ERR
+ "%s: Impossible packet size %d!\n",
+ dev->name, pkt_len);
+ lp->stats.rx_errors++;
+ } else if (pkt_len < 60) {
+ if (netif_msg_rx_err(lp))
+ printk(KERN_ERR "%s: Runt packet!\n",
+ dev->name);
+ lp->stats.rx_errors++;
+ } else {
+ int rx_in_place = 0;
+ if (--rx_work_limit < 0) {
+ goto not_done;
+ }
+ if (pkt_len > rx_copybreak) {
+ struct sk_buff *newskb;
+
+ if ((newskb =
+ dev_alloc_skb(PKT_BUF_SZ))) {
+ skb_reserve (newskb, 2);
+ skb = lp->rx_skbuff[entry];
+ pci_unmap_single(lp->pci_dev,
+ lp->
+ rx_dma_addr
+ [entry],
+ PKT_BUF_SZ-2,
+ PCI_DMA_FROMDEVICE);
+ skb_put (skb, pkt_len);
+ lp->rx_skbuff[entry] = newskb;
+ newskb->dev = dev;
+ lp->rx_dma_addr[entry] =
+ pci_map_single(lp->pci_dev,
+ newskb->data,
+ PKT_BUF_SZ -
+ 2,
+ PCI_DMA_FROMDEVICE);
+ lp->rx_ring[entry].base =
+ le32_to_cpu(lp->
+ rx_dma_addr
+ [entry]);
+ rx_in_place = 1;
+ } else
+ skb = NULL;
+ } else {
+ skb = dev_alloc_skb(pkt_len+2);
+ }
+
+ if (skb == NULL) {
+ int i;
+ if (netif_msg_drv(lp))
+ printk(KERN_ERR
+ "%s: Memory squeeze, deferring packet.\n",
+ dev->name);
+ for (i = 0; i < lp->rx_ring_size; i++)
+ if ((short)
+ le16_to_cpu(lp->
+ rx_ring[(entry +
+ i)
+ & lp->
+ rx_mod_mask].
+ status) < 0)
+ break;
+
+ if (i > lp->rx_ring_size -2) {
+ lp->stats.rx_dropped++;
+ lp->rx_ring[entry].status |=
+ le16_to_cpu(0x8000);
+ wmb(); /* Make sure adapter sees owner change */
+ lp->cur_rx++;
+ }
+ goto oom;
+ }
+ skb->dev = dev;
+ if (!rx_in_place) {
+ skb_reserve(skb,2); /* 16 byte align */
+ skb_put(skb,pkt_len); /* Make room */
+ pci_dma_sync_single_for_cpu(lp->pci_dev,
+ lp->
+ rx_dma_addr
+ [entry],
+ PKT_BUF_SZ -
+ 2,
+ PCI_DMA_FROMDEVICE);
+ eth_copy_and_sum(skb,
+ (unsigned char *)(lp->
+ rx_skbuff
+ [entry]->
+ data),
+ pkt_len,0);
+ pci_dma_sync_single_for_device(lp->
+ pci_dev,
+ lp->
+ rx_dma_addr
+ [entry],
+ PKT_BUF_SZ
+ - 2,
+ PCI_DMA_FROMDEVICE);
+ }
+ lp->stats.rx_bytes += skb->len;
+ skb->protocol=eth_type_trans(skb,dev);
+ dev->last_rx = jiffies;
+ netif_receive_skb(skb);
+ lp->stats.rx_packets++;
+ }
+ }
+ /*
+ * The docs say that the buffer length isn't touched, but Andrew Boyd
+ * of QNX reports that some revs of the 79C965 clear it.
+ */
+ lp->rx_ring[entry].buf_length = le16_to_cpu(2-PKT_BUF_SZ);
+ wmb(); /* Make sure owner changes after all others are visible */
+ lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+ entry = (++lp->cur_rx) & lp->rx_mod_mask;
+ received++;
+ }
+ } while(lp->a.read_csr (ioaddr, 0) & 0x1400);
+
+done:
+
+ dev->quota -= received;
+ *budget -= received;
+
+ /* Remove us from polling list and enable RX intr. */
+ netif_rx_complete(dev);
+ enable_rx_and_norxbuff_ints(dev);
+
+ return 0;
+
+not_done:
+ if (!received) {
+ received = dev->quota; /* Not to happen */
+ }
+ dev->quota -= received;
+ *budget -= received;
+
+ return 1;
+
+oom: /* Executed with RX ints disabled */
+
+ /* Start timer, stop polling, but do not enable rx interrupts. */
+ mod_timer(&lp->oom_timer, jiffies+1);
+
+ /* remove ourselves from the polling list */
+ netif_rx_complete(dev);
+
+ return 0;
+}
+#endif
+
static u16 pcnet32_wio_read_csr(unsigned long addr, int index)
{
outw(index, addr + PCNET32_WIO_RAP);
@@ -1284,6 +1504,12 @@
lp->mii_if.mdio_read = mdio_read;
lp->mii_if.mdio_write = mdio_write;

+#ifdef CONFIG_PCNET32_NAPI
+ init_timer(&lp->oom_timer);
+ lp->oom_timer.data = (unsigned long)dev;
+ lp->oom_timer.function = oom_timer;
+#endif
+
if (fdx && !(lp->options & PCNET32_PORT_ASEL) &&
((cards_found >= MAX_UNITS) || full_duplex[cards_found]))
lp->options |= PCNET32_PORT_FD;
@@ -1396,6 +1622,10 @@
dev->ethtool_ops = &pcnet32_ethtool_ops;
dev->tx_timeout = pcnet32_tx_timeout;
dev->watchdog_timeo = (5 * HZ);
+#ifdef CONFIG_PCNET32_NAPI
+ dev->poll = pcnet32_poll;
+ dev->weight = 16;
+#endif

#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = pcnet32_poll_controller;
@@ -2004,6 +2234,38 @@
return 0;
}

+void
+disable_rx_and_norxbuff_ints(struct net_device *dev)
+{
+ u16 csr3,rap;
+ unsigned long ioaddr;
+ struct pcnet32_private *lp;
+
+ ioaddr = dev->base_addr;
+ lp = dev->priv;
+ rap = lp->a.read_rap(ioaddr);
+ csr3 = lp->a.read_csr (ioaddr, 3);
+ lp->a.write_csr (ioaddr, 3, csr3 | 0x1400); // Set the MISSM and RINTM bits
+ lp->a.write_rap (ioaddr,rap);
+}
+
+void
+enable_rx_and_norxbuff_ints(struct net_device *dev)
+{
+ u16 csr3,rap;
+ unsigned long ioaddr;
+ struct pcnet32_private *lp;
+
+ ioaddr = dev->base_addr;
+ lp = dev->priv;
+ rap = lp->a.read_rap(ioaddr);
+ csr3 = lp->a.read_csr (ioaddr, 3);
+ lp->a.write_csr (ioaddr, 3, csr3 & ~0x1400); // Clear the MISSM and RINTM bits
+ /* Set interrupt enable. */
+ lp->a.write_csr (ioaddr, 0, 0x0040);
+ lp->a.write_rap (ioaddr,rap);
+}
+
/* The PCNET32 interrupt handler. */
static irqreturn_t
pcnet32_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -2033,7 +2295,11 @@
break; /* PCMCIA remove happened */
}
/* Acknowledge all of the current interrupt sources ASAP. */
+#ifdef CONFIG_PCNET32_NAPI
+ lp->a.write_csr (ioaddr, 0, csr0 & ~0x144f);
+#else
lp->a.write_csr(ioaddr, 0, csr0 & ~0x004f);
+#endif

must_restart = 0;

@@ -2042,8 +2308,18 @@
"%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
dev->name, csr0, lp->a.read_csr(ioaddr, 0));

- if (csr0 & 0x0400) /* Rx interrupt */
+ if (csr0 & 0x0400) { /* Rx interrupt */
+#ifdef CONFIG_PCNET32_NAPI
+ if(!lp->rl_active) {
+ disable_rx_and_norxbuff_ints(dev);
+ netif_rx_schedule(dev);
+ } else {
+ lp->a.write_csr (ioaddr, 0, 0x0400);
+ }
+#else
pcnet32_rx(dev);
+#endif
+ }

if (csr0 & 0x0200) { /* Tx-done interrupt */
unsigned int dirty_tx = lp->dirty_tx;
@@ -2161,7 +2437,16 @@
* interrupt, but a missed frame interrupt sooner or later.
* So we try to clean up our receive ring here.
*/
+#ifdef CONFIG_PCNET32_NAPI
+ if(!lp->rl_active) {
+ disable_rx_and_norxbuff_ints(dev);
+ netif_rx_schedule(dev);
+ } else {
+ lp->a.write_csr (ioaddr, 0, 0x1000);
+ }
+#else
pcnet32_rx(dev);
+#endif
lp->stats.rx_errors++; /* Missed a Rx frame. */
}
if (csr0 & 0x0800) {
@@ -2194,6 +2479,7 @@
return IRQ_HANDLED;
}

+#ifndef CONFIG_PCNET32_NAPI
static int pcnet32_rx(struct net_device *dev)
{
struct pcnet32_private *lp = dev->priv;
@@ -2349,6 +2635,7 @@

return 0;
}
+#endif

static int pcnet32_close(struct net_device *dev)
{
@@ -2358,6 +2645,9 @@
unsigned long flags;

del_timer_sync(&lp->watchdog_timer);
+#ifdef CONFIG_PCNET32_NAPI
+ del_timer_sync(&lp->oom_timer);
+#endif

netif_stop_queue(dev);