[PATCH] via-rhine.c (softnet)

From: Justin (jguyett@andrew.cmu.edu)
Date: Tue Feb 15 2000 - 12:45:53 EST


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