Although changing the one kfree_skb in the interrupt handler to
kfree_skb_irq fixed the kernel warning messages, ping -f would crash it.
This patch seems to make it stable, but it's not very minimal.
Justin
--- linux-2.3.45/drivers/net/via-rhine.c Tue Feb 15 10:07:12 2000
+++ linux/drivers/net/via-rhine.c Tue Feb 15 12:26:00 2000
@@ -1,6 +1,9 @@
/* via-rhine.c: A Linux Ethernet device driver for VIA Rhine family chips. */
/*
Written 1998-1999 by Donald Becker.
+ Modified, general cleanup for softnet, jguyett@andrew.cmu.edu
+ If there's a bug in this version that wasn't in the original,
+ I want to know.
This software may be used and distributed according to the terms
of the GNU Public License (GPL), incorporated herein by reference.
@@ -20,7 +23,7 @@
*/
static const char *versionA =
-"via-rhine.c:v1.01 2/27/99 Written by Donald Becker\n";
+"via-rhine.c:v1.01(softnet) 2/27/99 Written by Donald Becker\n";
static const char *versionB =
" http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n";
@@ -349,6 +352,7 @@
static void init_ring(struct net_device *dev);
static int start_tx(struct sk_buff *skb, struct net_device *dev);
static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
+static int netdev_tx(struct net_device *dev);
static int netdev_rx(struct net_device *dev);
static void netdev_error(struct net_device *dev, int intr_status);
static void set_rx_mode(struct net_device *dev);
@@ -487,14 +491,17 @@
/* Make certain the descriptor lists are cache-aligned. */
np = (void *)(((long)kmalloc(sizeof(*np), GFP_KERNEL) + 31) & ~31);
- /* FIXME! check return !!! */
+ if (np == NULL) {
+ kfree(dev);
+ return -ENOMEM;
+ }
memset(np, 0, sizeof(*np));
dev->priv = np;
+ spin_lock_init (&np->lock);
np->next_module = root_net_dev;
root_net_dev = dev;
- np->lock = SPIN_LOCK_UNLOCKED;
np->chip_id = chip_id;
if (dev->mem_start)
@@ -521,7 +528,7 @@
dev->get_stats = &get_stats;
dev->set_multicast_list = &set_rx_mode;
dev->do_ioctl = &mii_ioctl;
- dev->tx_timeout = tx_timeout;
+ dev->tx_timeout = &tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
if (cap_tbl[np->chip_id].flags & CanHaveMII) {
@@ -619,7 +626,6 @@
dev->if_port = np->default_port;
netif_start_queue(dev);
- np->in_interrupt = 0;
set_rx_mode(dev);
@@ -657,11 +663,12 @@
struct netdev_private *np = (struct netdev_private *)dev->priv;
long ioaddr = dev->base_addr;
int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+ int negotiated = mii_reg5 & np->advertising;
int duplex;
if (np->duplex_lock || mii_reg5 == 0xffff)
return;
- duplex = (mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040;
+ duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
if (np->full_duplex != duplex) {
np->full_duplex = duplex;
if (debug)
@@ -749,7 +756,6 @@
np->rx_ring[i].rx_status = 0;
np->rx_ring[i].rx_length = DescOwn;
}
- np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
for (i = 0; i < TX_RING_SIZE; i++) {
np->tx_skbuff[i] = 0;
@@ -767,12 +773,16 @@
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
unsigned entry;
+ unsigned long flags;
/* Caution: the write order is important here, set the field
with the "ownership" bits last. */
+ /* lock eth irq */
+ spin_lock_irqsave (&np->lock, flags);
+
/* Calculate the next Tx descriptor entry. */
- entry = np->cur_tx % TX_RING_SIZE;
+ entry = np->cur_tx;
np->tx_skbuff[entry] = skb;
@@ -789,23 +799,24 @@
(skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN);
np->tx_ring[entry].tx_own = DescOwn;
- np->cur_tx++;
+ np->cur_tx = (np->cur_tx + 1) % TX_RING_SIZE;
/* Non-x86 Todo: explicitly flush cache lines here. */
/* Wake the potentially-idle transmit channel. */
writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
- if (np->cur_tx - np->dirty_tx < TX_RING_SIZE - 1)
- netif_start_queue(dev); /* Typical path */
- else
+ if (np->cur_tx == np->dirty_tx) {
+ netif_stop_queue(dev);
np->tx_full = 1;
+ }
dev->trans_start = jiffies;
if (debug > 4) {
printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
dev->name, np->cur_tx, entry);
}
+ spin_unlock_irqrestore (&np->lock, flags);
return 0;
}
@@ -814,11 +825,10 @@
static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
{
struct net_device *dev = (struct net_device *)dev_instance;
- struct netdev_private *np;
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
long ioaddr, boguscnt = max_interrupt_work;
ioaddr = dev->base_addr;
- np = (struct netdev_private *)dev->priv;
spin_lock (&np->lock);
@@ -835,13 +845,43 @@
if (intr_status == 0)
break;
+ if (intr_status & (IntrTxDone | IntrTxAbort | IntrTxUnderrun |
+ IntrTxAborted))
+ netdev_tx(dev);
+
if (intr_status & (IntrRxDone | IntrRxErr | IntrRxDropped |
IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf))
netdev_rx(dev);
- for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
- int entry = np->dirty_tx % TX_RING_SIZE;
- int txstatus;
+ /* Abnormal error summary/uncommon events handlers. */
+ if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange |
+ IntrStatsMax | IntrTxAbort | IntrTxUnderrun))
+ netdev_error(dev, intr_status);
+
+ if (--boguscnt < 0) {
+ printk(KERN_WARNING "%s: Too much work at interrupt, "
+ "status=0x%4.4x.\n",
+ dev->name, intr_status);
+ break;
+ }
+ } while (1);
+
+ if (debug > 3)
+ printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+ dev->name, readw(ioaddr + IntrStatus));
+
+ spin_unlock (&np->lock);
+}
+
+/* This routine is logically part of the interrupt handler, but isolated
+ for clarity and better register allocation. */
+static int netdev_tx(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ int entry = 0, txstatus = 0;
+
+ while (np->dirty_tx != np->cur_tx || np->tx_full) {
+ entry = np->dirty_tx;
if (np->tx_ring[entry].tx_own)
break;
txstatus = np->tx_ring[entry].tx_status;
@@ -865,35 +905,15 @@
np->stats.tx_packets++;
}
/* Free the original skb. */
- kfree_skb(np->tx_skbuff[entry]);
+ dev_kfree_skb_irq(np->tx_skbuff[entry]);
np->tx_skbuff[entry] = 0;
- }
- if (np->tx_full &&
- test_bit(LINK_STATE_XOFF, &dev->flags) &&
- np->cur_tx - np->dirty_tx < TX_RING_SIZE - 4) {
- /* The ring is no longer full, clear tbusy. */
+ np->dirty_tx = (np->dirty_tx + 1) % TX_RING_SIZE;
np->tx_full = 0;
- netif_wake_queue (dev);
- }
-
- /* Abnormal error summary/uncommon events handlers. */
- if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange |
- IntrStatsMax | IntrTxAbort | IntrTxUnderrun))
- netdev_error(dev, intr_status);
-
- if (--boguscnt < 0) {
- printk(KERN_WARNING "%s: Too much work at interrupt, "
- "status=0x%4.4x.\n",
- dev->name, intr_status);
- break;
}
- } while (1);
-
- if (debug > 3)
- printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
- dev->name, readw(ioaddr + IntrStatus));
+ if (!np->tx_full)
+ netif_wake_queue (dev);
- spin_unlock (&np->lock);
+ return(0);
}
/* This routine is logically part of the interrupt handler, but isolated
@@ -901,8 +921,11 @@
static int netdev_rx(struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
- int entry = np->cur_rx % RX_RING_SIZE;
- int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
+ int entry = 0;
+ int boguscnt = (np->dirty_rx + RX_RING_SIZE - np->cur_rx) % RX_RING_SIZE;
+
+ if (boguscnt == 0)
+ boguscnt = RX_RING_SIZE;
if (debug > 4) {
printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n",
@@ -914,6 +937,7 @@
struct rx_desc *desc = np->rx_head_desc;
int data_size = desc->rx_length;
u16 desc_status = desc->rx_status;
+ entry = np->cur_rx;
if (debug > 4)
printk(KERN_DEBUG " netdev_rx() status is %4.4x.\n",
@@ -968,14 +992,14 @@
dev->last_rx = jiffies;
np->stats.rx_packets++;
}
- entry = (++np->cur_rx) % RX_RING_SIZE;
- np->rx_head_desc = &np->rx_ring[entry];
+ np->cur_rx = (np->cur_rx + 1) % RX_RING_SIZE;
+ np->rx_head_desc = &np->rx_ring[np->cur_rx];
}
/* Refill the Rx ring buffers. */
- for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+ while (np->cur_rx != np->dirty_rx) {
struct sk_buff *skb;
- entry = np->dirty_rx % RX_RING_SIZE;
+ entry = np->dirty_rx;
if (np->rx_skbuff[entry] == NULL) {
skb = dev_alloc_skb(np->rx_buf_sz);
np->rx_skbuff[entry] = skb;
@@ -986,6 +1010,7 @@
}
np->rx_ring[entry].rx_status = 0;
np->rx_ring[entry].rx_length = DescOwn;
+ np->dirty_rx = (np->dirty_rx + 1) % RX_RING_SIZE;
}
/* Pre-emptively restart Rx engine. */
@@ -1034,7 +1059,7 @@
}
}
-static struct enet_statistics *get_stats(struct net_device *dev)
+static struct net_device_stats *get_stats(struct net_device *dev)
{
struct netdev_private *np = (struct netdev_private *)dev->priv;
long ioaddr = dev->base_addr;
@@ -1147,13 +1172,13 @@
np->rx_ring[i].rx_length = 0;
np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
if (np->rx_skbuff[i]) {
- kfree_skb(np->rx_skbuff[i]);
+ dev_kfree_skb(np->rx_skbuff[i]);
}
np->rx_skbuff[i] = 0;
}
for (i = 0; i < TX_RING_SIZE; i++) {
if (np->tx_skbuff[i])
- kfree_skb(np->tx_skbuff[i]);
+ dev_kfree_skb(np->tx_skbuff[i]);
np->tx_skbuff[i] = 0;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/
This archive was generated by hypermail 2b29 : Tue Feb 15 2000 - 21:00:30 EST