DORMANTno replies

[PATCH 2.4.25] pcnet32.c handle failures in open

From: Don Fry <hidden>
Date: 2004-02-21 00:26:31

If there are errors in the open routine, the driver does not correctly clean
up and free resources. 

This also removes an unnecessary netif_wake_queue, correctly supports the
debug parameter, and updates the version and date.
--- linux-2.4.25/drivers/net/tool.pcnet32.c	Thu Feb 19 16:17:21 2004
+++ linux-2.4.25/drivers/net/pcnet32.c	Fri Feb 20 10:45:30 2004
@@ -22,8 +22,8 @@
  *************************************************************************/
 
 #define DRV_NAME	"pcnet32"
-#define DRV_VERSION	"1.27a"
-#define DRV_RELDATE	"10.02.2002"
+#define DRV_VERSION	"1.28"
+#define DRV_RELDATE	"02.20.2004"
 #define PFX		DRV_NAME ": "
 
 static const char *version =
@@ -218,6 +218,11 @@
  *	   fix pci probe not increment cards_found
  *	   FD auto negotiate error workaround for xSeries250
  *	   clean up and using new mii module
+ * v1.28   20 Feb 2004 Don Fry <brazilnut@us.ibm.com>
+ *	   Jon Lewis <jonmason@us.ibm.com>, Chinmay Albal <albal@in.ibm.com>
+ *	   Now uses ethtool_ops, netif_msg_* and generic_mii_ioctl.
+ *	   Fixes bogus 'Bus master arbitration failure', pci_[un]map_single
+ *	   length errors, and transmit hangs.  Cleans up after errors in open.
  */
 
 
@@ -638,8 +643,10 @@
     chip_version = a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr,89) << 16);
     if (pcnet32_debug & NETIF_MSG_PROBE)
 	printk(KERN_INFO "  PCnet chip version is %#x.\n", chip_version);
-    if ((chip_version & 0xfff) != 0x003)
+    if ((chip_version & 0xfff) != 0x003) {
+	printk(KERN_INFO PFX "Unsupported chip version.\n");
 	return -ENODEV;
+    }
     
     /* initialize variables */
     fdx = mii = fset = dxsuflo = ltint = 0;
@@ -720,8 +727,10 @@
     }
     
     dev = alloc_etherdev(0);
-    if(!dev)
+    if (!dev) {
+	printk(KERN_ERR PFX "Memory allocation failed.\n");
 	return -ENOMEM;
+    }
 
     printk(KERN_INFO PFX "%s at %#3lx,", chipname, ioaddr);
 
@@ -794,6 +803,7 @@
     
     /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */
     if ((lp = pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL) {
+	printk(KERN_ERR PFX "Consistent memory allocation failed.\n");
 	release_region(ioaddr, PCNET32_TOTAL_SIZE);
 	return -ENOMEM;
     }
@@ -915,6 +925,7 @@
     unsigned long ioaddr = dev->base_addr;
     u16 val;
     int i;
+    int rc;
 
     if (dev->irq == 0 ||
 	request_irq(dev->irq, &pcnet32_interrupt,
@@ -923,8 +934,10 @@
     }
 
     /* Check for a valid station address */
-    if( !is_valid_ether_addr(dev->dev_addr) )
-	return -EINVAL;
+    if (!is_valid_ether_addr(dev->dev_addr)) {
+	rc = -EINVAL;
+	goto err_free_irq;
+    }
 
     /* Reset the PCNET32 */
     lp->a.reset (ioaddr);
@@ -998,8 +1011,10 @@
     lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
     lp->init_block.filter[0] = 0x00000000;
     lp->init_block.filter[1] = 0x00000000;
-    if (pcnet32_init_ring(dev))
-	return -ENOMEM;
+    if (pcnet32_init_ring(dev)) {
+	rc = -ENOMEM;
+	goto err_free_ring;
+    }
     
     /* Re-initialize the PCNET32, and start it when done. */
     lp->a.write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) &0xffff);
@@ -1029,6 +1044,28 @@
     MOD_INC_USE_COUNT;
     
     return 0;	/* Always succeed */
+
+err_free_ring:
+    /* free any allocated skbuffs */
+    for (i = 0; i < RX_RING_SIZE; i++) {
+	lp->rx_ring[i].status = 0;			    
+	if (lp->rx_skbuff[i]) {
+            pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2,
+		    PCI_DMA_FROMDEVICE);
+	    dev_kfree_skb(lp->rx_skbuff[i]);
+        }
+	lp->rx_skbuff[i] = NULL;
+        lp->rx_dma_addr[i] = 0;
+    }
+    /*
+     * Switch back to 16bit mode to avoid problems with dumb 
+     * DOS packet driver after a warm reboot
+     */
+    lp->a.write_bcr (ioaddr, 20, 4);
+
+err_free_irq:
+    free_irq(dev->irq, dev);
+    return rc;
 }
 
 /*
@@ -1097,6 +1134,7 @@
 	lp->tx_ring[i].status = 0;
         lp->tx_dma_addr[i] = 0;
     }
+    wmb(); /* Make sure all changes are visible */
 
     lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
     for (i = 0; i < 6; i++)
@@ -1223,9 +1261,7 @@
 
     dev->trans_start = jiffies;
 
-    if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
-	netif_wake_queue(dev);
-    else {
+    if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base != 0) {
 	lp->tx_full = 1;
 	netif_stop_queue(dev);
     }
@@ -1479,6 +1515,7 @@
 	 * 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) & RX_RING_MOD_MASK;
     }
@@ -1678,7 +1715,7 @@
 };
 
 MODULE_PARM(debug, "i");
-MODULE_PARM_DESC(debug, DRV_NAME " debug level (0-6)");
+MODULE_PARM_DESC(debug, DRV_NAME " debug level");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM_DESC(max_interrupt_work, DRV_NAME " maximum events handled per interrupt");  
 MODULE_PARM(rx_copybreak, "i");
@@ -1705,8 +1742,8 @@
 {
     printk(KERN_INFO "%s", version);
 
-    if (debug > 0)
-	pcnet32_debug = debug;
+    if (debug >= 0 && debug < (sizeof(int) - 1))
+	pcnet32_debug = 1 << debug;
 
     if ((tx_start_pt >= 0) && (tx_start_pt <= 3))
 	tx_start = tx_start_pt;
-- 
Don Fry
brazilnut@us.ibm.com
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help