Commit 3893fcd9 authored by Don Fry's avatar Don Fry Committed by Jeff Garzik

[PATCH] pcnet32 fix hang/crash with loopback test

If the pcnet32 interface is not up, running the loopback test may hang or
crash the system.  This patch provided by Jim Lewis fixes that problem.
Tested on ia32 and ppc systems.
parent ba4ccb41
...@@ -360,6 +360,7 @@ static void pcnet32_tx_timeout (struct net_device *dev); ...@@ -360,6 +360,7 @@ static void pcnet32_tx_timeout (struct net_device *dev);
static irqreturn_t pcnet32_interrupt(int, void *, struct pt_regs *); static irqreturn_t pcnet32_interrupt(int, void *, struct pt_regs *);
static int pcnet32_close(struct net_device *); static int pcnet32_close(struct net_device *);
static struct net_device_stats *pcnet32_get_stats(struct net_device *); static struct net_device_stats *pcnet32_get_stats(struct net_device *);
static void pcnet32_load_multicast(struct net_device *dev);
static void pcnet32_set_multicast_list(struct net_device *); static void pcnet32_set_multicast_list(struct net_device *);
static int pcnet32_ioctl(struct net_device *, struct ifreq *, int); static int pcnet32_ioctl(struct net_device *, struct ifreq *, int);
static void pcnet32_watchdog(struct net_device *); static void pcnet32_watchdog(struct net_device *);
...@@ -627,34 +628,40 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1) ...@@ -627,34 +628,40 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1)
struct pcnet32_access *a = &lp->a; /* access to registers */ struct pcnet32_access *a = &lp->a; /* access to registers */
ulong ioaddr = dev->base_addr; /* card base I/O address */ ulong ioaddr = dev->base_addr; /* card base I/O address */
struct sk_buff *skb; /* sk buff */ struct sk_buff *skb; /* sk buff */
int x, y, i; /* counters */ int x, i; /* counters */
int numbuffs = 4; /* number of TX/RX buffers and descs */ int numbuffs = 4; /* number of TX/RX buffers and descs */
u16 status = 0x8300; /* TX ring status */ u16 status = 0x8300; /* TX ring status */
u16 teststatus; /* test of ring status */
int rc; /* return code */ int rc; /* return code */
int size; /* size of packets */ int size; /* size of packets */
unsigned char *packet; /* source packet data */ unsigned char *packet; /* source packet data */
static int data_len = 60; /* length of source packets */ static int data_len = 60; /* length of source packets */
unsigned long flags; unsigned long flags;
unsigned long ticks;
*data1 = 1; /* status of test, default to fail */ *data1 = 1; /* status of test, default to fail */
rc = 1; /* default to fail */ rc = 1; /* default to fail */
if (netif_running(dev))
pcnet32_close(dev);
spin_lock_irqsave(&lp->lock, flags); spin_lock_irqsave(&lp->lock, flags);
lp->a.write_csr(ioaddr, 0, 0x7904);
del_timer_sync(&lp->watchdog_timer); /* Reset the PCNET32 */
lp->a.reset (ioaddr);
netif_stop_queue(dev); /* switch pcnet32 to 32bit mode */
lp->a.write_bcr (ioaddr, 20, 2);
lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
lp->init_block.filter[0] = 0;
lp->init_block.filter[1] = 0;
/* purge & init rings but don't actually restart */ /* purge & init rings but don't actually restart */
pcnet32_restart(dev, 0x0000); pcnet32_restart(dev, 0x0000);
lp->a.write_csr(ioaddr, 0, 0x0004); /* Set STOP bit */ lp->a.write_csr(ioaddr, 0, 0x0004); /* Set STOP bit */
x = a->read_bcr(ioaddr, 32); /* set internal loopback in BSR32 */
x = x | 0x00000002;
a->write_bcr(ioaddr, 32, x);
/* Initialize Transmit buffers. */ /* Initialize Transmit buffers. */
size = data_len + 15; size = data_len + 15;
for (x=0; x<numbuffs; x++) { for (x=0; x<numbuffs; x++) {
...@@ -668,19 +675,21 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1) ...@@ -668,19 +675,21 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1)
skb_put(skb, size); /* create space for data */ skb_put(skb, size); /* create space for data */
lp->tx_skbuff[x] = skb; lp->tx_skbuff[x] = skb;
lp->tx_ring[x].length = le16_to_cpu(-skb->len); lp->tx_ring[x].length = le16_to_cpu(-skb->len);
lp->tx_ring[x].misc = 0x00000000; lp->tx_ring[x].misc = 0;
/* put DA and SA into the skb */ /* put DA and SA into the skb */
for (i=0; i<12; i++) for (i=0; i<6; i++)
*packet++ = 0xff; *packet++ = dev->dev_addr[i];
for (i=0; i<6; i++)
*packet++ = dev->dev_addr[i];
/* type */ /* type */
*packet++ = 0x08; *packet++ = 0x08;
*packet++ = 0x06; *packet++ = 0x06;
/* packet number */ /* packet number */
*packet++ = x; *packet++ = x;
/* fill packet with data */ /* fill packet with data */
for (y=0; y<data_len; y++) for (i=0; i<data_len; i++)
*packet++ = y; *packet++ = i;
lp->tx_dma_addr[x] = pci_map_single(lp->pci_dev, skb->data, lp->tx_dma_addr[x] = pci_map_single(lp->pci_dev, skb->data,
skb->len, PCI_DMA_TODEVICE); skb->len, PCI_DMA_TODEVICE);
...@@ -690,20 +699,41 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1) ...@@ -690,20 +699,41 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1)
} }
} }
lp->a.write_csr(ioaddr, 0, 0x0002); /* Set STRT bit */ x = a->read_bcr(ioaddr, 32); /* set internal loopback in BSR32 */
spin_unlock_irqrestore(&lp->lock, flags); x = x | 0x0002;
a->write_bcr(ioaddr, 32, x);
mdelay(50); /* wait a bit */ lp->a.write_csr (ioaddr, 15, 0x0044); /* set int loopback in CSR15 */
spin_lock_irqsave(&lp->lock, flags); teststatus = le16_to_cpu(0x8000);
lp->a.write_csr(ioaddr, 0, 0x0004); /* Set STOP bit */ lp->a.write_csr(ioaddr, 0, 0x0002); /* Set STRT bit */
/* Check status of descriptors */
for (x=0; x<numbuffs; x++) {
ticks = 0;
rmb();
while ((lp->rx_ring[x].status & teststatus) && (ticks < 200)) {
spin_unlock_irqrestore(&lp->lock, flags);
mdelay(1);
spin_lock_irqsave(&lp->lock, flags);
rmb();
ticks++;
}
if (ticks == 200) {
if (netif_msg_hw(lp))
printk("%s: Desc %d failed to reset!\n",dev->name,x);
break;
}
}
lp->a.write_csr(ioaddr, 0, 0x0004); /* Set STOP bit */
wmb();
if (netif_msg_hw(lp) && netif_msg_pktdata(lp)) { if (netif_msg_hw(lp) && netif_msg_pktdata(lp)) {
printk(KERN_DEBUG "%s: RX loopback packets:\n", dev->name); printk(KERN_DEBUG "%s: RX loopback packets:\n", dev->name);
for (x=0; x<numbuffs; x++) { for (x=0; x<numbuffs; x++) {
printk(KERN_DEBUG "%s: Packet %d:\n", dev->name, x); printk(KERN_DEBUG "%s: Packet %d:\n", dev->name, x);
skb=lp->rx_skbuff[x]; skb = lp->rx_skbuff[x];
for (i=0; i<size; i++) { for (i=0; i<size; i++) {
printk("%02x ", *(skb->data+i)); printk("%02x ", *(skb->data+i));
} }
...@@ -736,19 +766,17 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1) ...@@ -736,19 +766,17 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1)
a->write_csr(ioaddr, 15, (x & ~0x0044)); /* reset bits 6 and 2 */ a->write_csr(ioaddr, 15, (x & ~0x0044)); /* reset bits 6 and 2 */
x = a->read_bcr(ioaddr, 32); /* reset internal loopback */ x = a->read_bcr(ioaddr, 32); /* reset internal loopback */
x = x & ~0x00000002; x = x & ~0x0002;
a->write_bcr(ioaddr, 32, x); a->write_bcr(ioaddr, 32, x);
pcnet32_restart(dev, 0x0042); /* resume normal operation */
netif_wake_queue(dev);
mod_timer(&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT);
/* Clear interrupts, and set interrupt enable. */
lp->a.write_csr(ioaddr, 0, 0x7940);
spin_unlock_irqrestore(&lp->lock, flags); spin_unlock_irqrestore(&lp->lock, flags);
if (netif_running(dev)) {
pcnet32_open(dev);
} else {
lp->a.write_bcr (ioaddr, 20, 4); /* return to 16bit mode */
}
return(rc); return(rc);
} /* end pcnet32_loopback_test */ } /* end pcnet32_loopback_test */
...@@ -1056,7 +1084,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared, ...@@ -1056,7 +1084,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
if (pcnet32_debug & NETIF_MSG_PROBE) { if (pcnet32_debug & NETIF_MSG_PROBE) {
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
printk(" %2.2x", dev->dev_addr[i] ); printk(" %2.2x", dev->dev_addr[i]);
/* Version 0x2623 and 0x2624 */ /* Version 0x2623 and 0x2624 */
if (((chip_version + 1) & 0xfffe) == 0x2624) { if (((chip_version + 1) & 0xfffe) == 0x2624) {
...@@ -1244,6 +1272,7 @@ pcnet32_open(struct net_device *dev) ...@@ -1244,6 +1272,7 @@ pcnet32_open(struct net_device *dev)
u16 val; u16 val;
int i; int i;
int rc; int rc;
unsigned long flags;
if (dev->irq == 0 || if (dev->irq == 0 ||
request_irq(dev->irq, &pcnet32_interrupt, request_irq(dev->irq, &pcnet32_interrupt,
...@@ -1251,6 +1280,7 @@ pcnet32_open(struct net_device *dev) ...@@ -1251,6 +1280,7 @@ pcnet32_open(struct net_device *dev)
return -EAGAIN; return -EAGAIN;
} }
spin_lock_irqsave(&lp->lock, flags);
/* Check for a valid station address */ /* Check for a valid station address */
if (!is_valid_ether_addr(dev->dev_addr)) { if (!is_valid_ether_addr(dev->dev_addr)) {
rc = -EINVAL; rc = -EINVAL;
...@@ -1331,8 +1361,8 @@ pcnet32_open(struct net_device *dev) ...@@ -1331,8 +1361,8 @@ pcnet32_open(struct net_device *dev)
} }
lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
lp->init_block.filter[0] = 0x00000000; pcnet32_load_multicast(dev);
lp->init_block.filter[1] = 0x00000000;
if (pcnet32_init_ring(dev)) { if (pcnet32_init_ring(dev)) {
rc = -ENOMEM; rc = -ENOMEM;
goto err_free_ring; goto err_free_ring;
...@@ -1371,6 +1401,7 @@ pcnet32_open(struct net_device *dev) ...@@ -1371,6 +1401,7 @@ pcnet32_open(struct net_device *dev)
offsetof(struct pcnet32_private, init_block)), offsetof(struct pcnet32_private, init_block)),
lp->a.read_csr(ioaddr, 0)); lp->a.read_csr(ioaddr, 0));
spin_unlock_irqrestore(&lp->lock, flags);
return 0; /* Always succeed */ return 0; /* Always succeed */
...@@ -1393,6 +1424,7 @@ pcnet32_open(struct net_device *dev) ...@@ -1393,6 +1424,7 @@ pcnet32_open(struct net_device *dev)
lp->a.write_bcr (ioaddr, 20, 4); lp->a.write_bcr (ioaddr, 20, 4);
err_free_irq: err_free_irq:
spin_unlock_irqrestore(&lp->lock, flags);
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
return rc; return rc;
} }
...@@ -1885,11 +1917,14 @@ pcnet32_close(struct net_device *dev) ...@@ -1885,11 +1917,14 @@ pcnet32_close(struct net_device *dev)
unsigned long ioaddr = dev->base_addr; unsigned long ioaddr = dev->base_addr;
struct pcnet32_private *lp = dev->priv; struct pcnet32_private *lp = dev->priv;
int i; int i;
unsigned long flags;
del_timer_sync(&lp->watchdog_timer); del_timer_sync(&lp->watchdog_timer);
netif_stop_queue(dev); netif_stop_queue(dev);
spin_lock_irqsave(&lp->lock, flags);
lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112); lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112);
if (netif_msg_ifdown(lp)) if (netif_msg_ifdown(lp))
...@@ -1905,6 +1940,8 @@ pcnet32_close(struct net_device *dev) ...@@ -1905,6 +1940,8 @@ pcnet32_close(struct net_device *dev)
*/ */
lp->a.write_bcr (ioaddr, 20, 4); lp->a.write_bcr (ioaddr, 20, 4);
spin_unlock_irqrestore(&lp->lock, flags);
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
/* free all allocated skbuffs */ /* free all allocated skbuffs */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment