* 3c59x 2.6 napi patch
@ 2003-09-21 15:22 Michele 'mydecay' Marchetto
0 siblings, 0 replies; only message in thread
From: Michele 'mydecay' Marchetto @ 2003-09-21 15:22 UTC (permalink / raw)
To: netdev, linux-net
[-- Attachment #1: Type: text/plain, Size: 183 bytes --]
hi all
this is the trivial patch to get napi work on 2.6.
it is designed for 3com 3c59x nic series.
regards
--
Michele 'mydecay' Marchetto
S.P.I.N.E. Group - www.spine-group.org
[-- Attachment #2: 3c59x.diff --]
[-- Type: text/plain, Size: 15445 bytes --]
--- 3c59x.c.old 2003-09-08 20:29:52.000000000 +0200
+++ 3c59x.c 2003-09-21 16:58:40.451344112 +0200
@@ -204,7 +204,7 @@
/* A few values that may be tweaked. */
/* Keep the ring sizes a power of two for efficiency. */
#define TX_RING_SIZE 16
-#define RX_RING_SIZE 32
+#define RX_RING_SIZE 128
#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
/* "Knobs" that adjust features and parameters. */
@@ -889,7 +889,7 @@
static int vortex_start_xmit(struct sk_buff *skb, struct net_device *dev);
static int boomerang_start_xmit(struct sk_buff *skb, struct net_device *dev);
static int vortex_rx(struct net_device *dev);
-static int boomerang_rx(struct net_device *dev);
+static int boomerang_poll(struct net_device *dev, int *budget);
static irqreturn_t vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static irqreturn_t boomerang_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int vortex_close(struct net_device *dev);
@@ -1419,6 +1419,8 @@
}
vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2;
vp->bus_master = 0; /* AKPM: vortex only */
+ dev->poll = boomerang_poll;
+ dev->weight = 16;
}
/* The 3c59x-specific entries in the device structure. */
@@ -2004,7 +2006,7 @@
printk(KERN_WARNING "%s: Updating statistics failed, disabling "
"stats as an interrupt source.\n", dev->name);
EL3WINDOW(5);
- outw(SetIntrEnb | (inw(ioaddr + 10) & ~StatsFull), ioaddr + EL3_CMD);
+ outw(vp->intr_enable, ioaddr + EL3_CMD);
vp->intr_enable &= ~StatsFull;
EL3WINDOW(7);
DoneDidThat++;
@@ -2306,6 +2308,57 @@
return IRQ_RETVAL(handled);
}
+inline int tx_ring_free(struct net_device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int tx = 0;
+ unsigned int dirty_tx = vp->dirty_tx;
+ long ioaddr = dev->base_addr;
+
+ while (vp->cur_tx - dirty_tx > 0) {
+ int entry = dirty_tx % TX_RING_SIZE;
+#if 1 /* AKPM: the latter is faster, but cyclone-only */
+ if (inl(ioaddr + DownListPtr) ==
+ vp->tx_ring_dma + entry * sizeof(struct boom_tx_desc))
+ break; /* It still hasn't been processed. */
+#else
+ if ((vp->tx_ring[entry].status & DN_COMPLETE) == 0)
+ break; /* It still hasn't been processed. */
+#endif
+
+ if (vp->tx_skbuff[entry]) {
+ struct sk_buff *skb = vp->tx_skbuff[entry];
+#if DO_ZEROCOPY
+ int i;
+ for (i=0; i<=skb_shinfo(skb)->nr_frags; i++)
+ pci_unmap_single(VORTEX_PCI(vp),
+ le32_to_cpu(vp->tx_ring[entry].frag[i].addr),
+ le32_to_cpu(vp->tx_ring[entry].frag[i].length)&0xFFF,
+ PCI_DMA_TODEVICE);
+#else
+ pci_unmap_single(vp->pdev,
+ le32_to_cpu(vp->tx_ring[entry].addr), skb->len, PCI_DMA_TODEVICE);
+#endif
+ dev_kfree_skb_irq(skb);
+ vp->tx_skbuff[entry] = 0;
+ } else {
+ printk(KERN_DEBUG "boomerang_interrupt: no skb!\n");
+ }
+ /* vp->stats.tx_packets++; Counted below. */
+ dirty_tx++;
+ tx++;
+ }
+
+ if (vp->cur_tx - dirty_tx <= TX_RING_SIZE - 1) {
+ if (vortex_debug > 6)
+ printk(KERN_DEBUG "boomerang_interrupt: wake queue\n");
+ netif_wake_queue (dev);
+ }
+
+ vp->dirty_tx = dirty_tx;
+ return tx;
+}
+
/*
* This is the ISR for the boomerang series chips.
* full_bus_master_tx == 1 && full_bus_master_rx == 1
@@ -2352,57 +2405,24 @@
dev->name, status, inb(ioaddr + Timer));
do {
if (vortex_debug > 5)
- printk(KERN_DEBUG "%s: In interrupt loop, status %4.4x.\n",
- dev->name, status);
+ printk(KERN_DEBUG "%s: In interrupt loop, status %4.4x.\n",
+ dev->name, status);
+
if (status & UpComplete) {
outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
- if (vortex_debug > 5)
- printk(KERN_DEBUG "boomerang_interrupt->boomerang_rx\n");
- boomerang_rx(dev);
+ if (netif_rx_schedule_prep(dev)) {
+ /* Do masking under poll protection. */
+ vp->intr_enable &= ~(UpComplete | StatsFull);
+ outw(vp->intr_enable, ioaddr + EL3_CMD);
+ if (vortex_debug > 5)
+ printk(KERN_DEBUG "boomerang_interrupt->boomerang_rx\n");
+ __netif_rx_schedule(dev);
+ }
}
if (status & DownComplete) {
- unsigned int dirty_tx = vp->dirty_tx;
-
outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
- while (vp->cur_tx - dirty_tx > 0) {
- int entry = dirty_tx % TX_RING_SIZE;
-#if 1 /* AKPM: the latter is faster, but cyclone-only */
- if (inl(ioaddr + DownListPtr) ==
- vp->tx_ring_dma + entry * sizeof(struct boom_tx_desc))
- break; /* It still hasn't been processed. */
-#else
- if ((vp->tx_ring[entry].status & DN_COMPLETE) == 0)
- break; /* It still hasn't been processed. */
-#endif
-
- if (vp->tx_skbuff[entry]) {
- struct sk_buff *skb = vp->tx_skbuff[entry];
-#if DO_ZEROCOPY
- int i;
- for (i=0; i<=skb_shinfo(skb)->nr_frags; i++)
- pci_unmap_single(VORTEX_PCI(vp),
- le32_to_cpu(vp->tx_ring[entry].frag[i].addr),
- le32_to_cpu(vp->tx_ring[entry].frag[i].length)&0xFFF,
- PCI_DMA_TODEVICE);
-#else
- pci_unmap_single(VORTEX_PCI(vp),
- le32_to_cpu(vp->tx_ring[entry].addr), skb->len, PCI_DMA_TODEVICE);
-#endif
- dev_kfree_skb_irq(skb);
- vp->tx_skbuff[entry] = 0;
- } else {
- printk(KERN_DEBUG "boomerang_interrupt: no skb!\n");
- }
- /* vp->stats.tx_packets++; Counted below. */
- dirty_tx++;
- }
- vp->dirty_tx = dirty_tx;
- if (vp->cur_tx - dirty_tx <= TX_RING_SIZE - 1) {
- if (vortex_debug > 6)
- printk(KERN_DEBUG "boomerang_interrupt: wake queue\n");
- netif_wake_queue (dev);
- }
+ tx_ring_free(dev);
}
/* Check for all uncommon interrupts at once. */
@@ -2508,20 +2528,62 @@
}
static int
-boomerang_rx(struct net_device *dev)
+boomerang_refill_rx(struct net_device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int entry;
+ int refilled = 0;
+
+ /* Refill the Rx ring buffers. */
+ for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
+ struct sk_buff *skb;
+ entry = vp->dirty_rx % RX_RING_SIZE;
+ if (vp->rx_skbuff[entry] == NULL) {
+ skb = dev_alloc_skb(PKT_BUF_SZ);
+ if (skb == NULL)
+ break;
+
+ skb->dev = dev; /* Mark as being used by this device. */
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ vp->rx_ring[entry].addr = cpu_to_le32(pci_map_single(VORTEX_PCI(vp), skb->tail, PKT_BUF_SZ, PCI_DMA_FROMDEVICE));
+ vp->rx_skbuff[entry] = skb;
+
+ refilled++;
+ }
+ vp->rx_ring[entry].status = 0; /* Clear complete bit. */
+ outw(UpUnstall, ioaddr + EL3_CMD);
+ }
+
+ /* @@@ restart RX ? */
+
+ return refilled;
+}
+
+static int
+boomerang_poll(struct net_device *dev, int *budget)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
int entry = vp->cur_rx % RX_RING_SIZE;
long ioaddr = dev->base_addr;
int rx_status;
- int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx;
+ int rx_work_limit = *budget;
+ int received = 0;
+
if (vortex_debug > 5)
printk(KERN_DEBUG "boomerang_rx(): status %4.4x\n", inw(ioaddr+EL3_STATUS));
+ if (rx_work_limit > dev->quota)
+ rx_work_limit = dev->quota;
+
+restart:
+
while ((rx_status = le32_to_cpu(vp->rx_ring[entry].status)) & RxDComplete){
- if (--rx_work_limit < 0)
+ if (vp->dirty_rx + RX_RING_SIZE == vp->cur_rx)
break;
+ if (--rx_work_limit < 0)
+ goto not_done;
if (rx_status & RxDError) { /* Error, update stats. */
unsigned char rx_error = rx_status >> 16;
if (vortex_debug > 2)
@@ -2571,38 +2633,111 @@
vp->rx_csumhits++;
}
}
- netif_rx(skb);
+ netif_receive_skb(skb);
dev->last_rx = jiffies;
vp->stats.rx_packets++;
}
+ received++;
+
entry = (++vp->cur_rx) % RX_RING_SIZE;
+ if (vp->cur_rx - vp->dirty_rx > RX_RING_SIZE/4)
+ boomerang_refill_rx(dev);
}
- /* Refill the Rx ring buffers. */
- for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
- struct sk_buff *skb;
- entry = vp->dirty_rx % RX_RING_SIZE;
- if (vp->rx_skbuff[entry] == NULL) {
- skb = dev_alloc_skb(PKT_BUF_SZ);
- if (skb == NULL) {
- static unsigned long last_jif;
- if ((jiffies - last_jif) > 10 * HZ) {
- printk(KERN_WARNING "%s: memory shortage\n", dev->name);
- last_jif = jiffies;
- }
- if ((vp->cur_rx - vp->dirty_rx) == RX_RING_SIZE)
- mod_timer(&vp->rx_oom_timer, RUN_AT(HZ * 1));
- break; /* Bad news! */
- }
- skb->dev = dev; /* Mark as being used by this device. */
- skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- vp->rx_ring[entry].addr = cpu_to_le32(pci_map_single(VORTEX_PCI(vp), skb->tail, PKT_BUF_SZ, PCI_DMA_FROMDEVICE));
- vp->rx_skbuff[entry] = skb;
- }
- vp->rx_ring[entry].status = 0; /* Clear complete bit. */
- outw(UpUnstall, ioaddr + EL3_CMD);
+
+ boomerang_refill_rx(dev);
+ if (vp->rx_skbuff[vp->dirty_rx % RX_RING_SIZE] == NULL)
+ goto oom;
+
+ #if 0
+ /* @@@ disable receive interrupt */
+ if (jiffies - dev->last_rx == 0) {
+ vp->intr_enable &= ~(UpComplete | StatsFull);
+ outw(vp->intr_enable, ioaddr + EL3_CMD);
+ goto not_done;
}
- return 0;
-}
+#endif
+
+/* MAJOR QUESTION: I see this driver uses vp->lock even around __RX__ intrs!
+ * If this is really necessary, it is fatal flaw. I am afraid such device
+ * cannot be handled with NAPI at all. Or for beginning this funny lock
+ * is to be removed for normal irq driven case.
+ */
+
+/* So, I assume that 3c95x loses irqs when being with disabled intrs
+ * (but still hope that the assumption is wrong and this is not required.
+ * Lennert, did you really try the scheme exactly matching tulip?
+ * Actually, I see one place where boomerang could lose irq even if semantics
+ * of its irq mask is right i.e. tulip-like. It is vp->intr_enable.
+ * Tulip does _not_ hold mirror of irq mask in a state variable. See?
+ * 3com does and hence can occasionally corrupt it while concurrent ands/ors.
+ * If you remove clearing of rx bits in vp->intr_enable, it can be repaired
+ * without ugly tricks).
+ *
+ * OK. Assume the worst variant.
+ */
+
+ /* ALL THE STATE UPDATES MUST be done before netif_rx_complete(),
+ * which releases the device for another contexts. */
+
+ dev->quota -= received;
+
+ /* Change intr_enable in context serialized wrt irq. See also above.
+ * However look at vortex_error... intr_enable is changed there too... */
+
+ vp->intr_enable |= UpComplete | StatsFull; /* @@@ */
+ outw(vp->intr_enable, ioaddr + EL3_CMD);
+
+ netif_rx_complete(dev);
+ /* ---< now this function can be reentered on another cpu. */
+
+ /* In the case of misfortune (packet arrived in race window), we
+ * try to reschedule poll. If the irq already arrived and this happened
+ * after netif_rx_complete() released device poll is already scheduled
+ * (on this or on another cpu) and we just return. Otherwise, we undo
+ * state changes and return to ring processing until quota exhausts.
+ *
+ * WARNING. IRQ status cannot be checked here, because irq handler
+ * has to reset it before exit from irq handler no matter poll is
+ * scheduled or it did not.
+ */
+
+ if ((le32_to_cpu(vp->rx_ring[vp->cur_rx % RX_RING_SIZE].status) & RxDComplete)
+ && vp->dirty_rx + RX_RING_SIZE != vp->cur_rx
+ && netif_rx_reschedule(dev, received)) {
+ /* ACK may be pure loss of time. */
+ outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
+ vp->intr_enable &= ~(UpComplete | StatsFull);
+ outw(vp->intr_enable, ioaddr + EL3_CMD);
+ entry = vp->cur_rx % RX_RING_SIZE;
+ goto restart;
+ }
+
+ *budget -= received;
+
+ return 0;
+
+not_done:
+ if (vp->cur_rx - vp->dirty_rx > RX_RING_SIZE/2 ||
+ vp->rx_skbuff[vp->dirty_rx % RX_RING_SIZE] == NULL)
+ boomerang_refill_rx(dev);
+
+ if (!received)
+ received = dev->quota;
+
+ dev->quota -= received;
+ *budget -= received;
+
+ return 1;
+
+oom:
+ if (vortex_debug > 1)
+ printk(KERN_WARNING "%s: in rx suspend mode\n", dev->name);
+
+ mod_timer(&vp->rx_oom_timer, RUN_AT(HZ * 1));
+ netif_rx_complete(dev);
+
+ return 0;
+ }
/*
* If we've hit a total OOM refilling the Rx ring we poll once a second
@@ -2612,16 +2747,35 @@
rx_oom_timer(unsigned long arg)
{
struct net_device *dev = (struct net_device *)arg;
+#if 0
struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int budget = 1;
+
+/********************* FULL SHIT!!! ***********************************
+ boomerang_poll cannot be called with vp->lock held, it is 100% deadlock.
+ Also, it cannot be called with irq disabled, it is 100% crash.
+ BTW: no doubts it is the case when Lennert saw BUG() asserting
+ unscheduled poll, this boomerang_poll is surely unscheduled.
+ **********************************************************************/
+ spin_lock_irq(&vp->lock);
- spin_lock_irq(&vp->lock);
if ((vp->cur_rx - vp->dirty_rx) == RX_RING_SIZE) /* This test is redundant, but makes me feel good */
- boomerang_rx(dev);
+ boomerang_poll(dev, &budget);
if (vortex_debug > 1) {
printk(KERN_DEBUG "%s: rx_oom_timer %s\n", dev->name,
((vp->cur_rx - vp->dirty_rx) != RX_RING_SIZE) ? "succeeded" : "retrying");
}
spin_unlock_irq(&vp->lock);
+#else
+ /* Essentially, this is all, which is possible to make here. */
+ netif_rx_schedule(dev);
+
+ /* Robert, oom_timer in tilip of 011015 suffers of similar desease.
+ * We _cannot_ refill ring from timer not serializing it wrt refill from
+ * poll and/or irq. So, either add necessary locking bits f.e. like
+ * it is made in acenic or return to tulip's internal timer.
+ */
+#endif
}
static void
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2003-09-21 15:22 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-09-21 15:22 3c59x 2.6 napi patch Michele 'mydecay' Marchetto
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.