Received: by oss.sgi.com id ; Sun, 25 Jun 2000 04:18:58 -0700 Received: from [203.126.247.144] ([203.126.247.144]:48287 "EHLO zsngs001") by oss.sgi.com with ESMTP id ; Sun, 25 Jun 2000 04:18:41 -0700 Received: from zsngd101.asiapac.nortel.com (actually znsgd101) by zsngs001; Sun, 25 Jun 2000 19:17:39 +0800 Received: from zctwb003.asiapac.nortel.com ([47.152.32.111]) by zsngd101.asiapac.nortel.com with SMTP (Microsoft Exchange Internet Mail Service Version 5.5.2650.21) id NQ12P4LX; Sun, 25 Jun 2000 19:17:43 +0800 Received: from pwold011.asiapac.nortel.com ([47.181.193.45]) by zctwb003.asiapac.nortel.com with SMTP (Microsoft Exchange Internet Mail Service Version 5.5.2650.21) id NCLF8N4R; Sun, 25 Jun 2000 21:17:45 +1000 Received: from uow.edu.au (IDENT:akpm@[47.181.194.209]) by pwold011.asiapac.nortel.com (8.9.3/8.9.3) with ESMTP id VAA12816; Sun, 25 Jun 2000 21:16:13 +1000 Message-ID: <3955EB11.519DF76B@uow.edu.au> Date: Sun, 25 Jun 2000 21:20:49 +1000 X-Sybari-Space: 00000000 00000000 00000000 From: Andrew Morton X-Mailer: Mozilla 4.7 [en] (X11; I; Linux 2.2.14-15mdk i586) X-Accept-Language: en MIME-Version: 1.0 To: Alan Cox CC: "netdev@oss.sgi.com" , Andreas Tobler Subject: [patch] 3c59x.c for 2.2.17 Content-Type: multipart/mixed; boundary="------------A028613A1132A2E773E9E93A" X-Orig: Sender: owner-netdev@oss.sgi.com Precedence: bulk Return-Path: X-Orcpt: rfc822;netdev-outgoing This is a multi-part message in MIME format. --------------A028613A1132A2E773E9E93A Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Hi, Alan. - Andrey's tester reported that the driver was still oopsing every three days on vortex-specific code for a 3c905. So I've split the ISR into vortex_interrupt() and boomerang_interrupt() as per the 2.3/2.4 driver. - The driver would crash the machine if all dev_alloc_skb()'s failed during open(). So I simply fail the open() if we can't preallocate all the skb's. - The problem reported by Mark Hemment where the rx path would die if 32 successive dev_alloc_skb()'s failed has been semi-kludgily fixed by detecting this situation in the tx interrupt and deliberately calling the rx ISR. This means that in cruel OOM situations we're relying on Tx interrupts to initiate polling for available memory. This can take some time if TCP has backed off a long way, but it recovers eventually. - Put an explicit "are we interrupting" test at the start is the ISRs to improve efficiency during PCI interrupt sharing. Also avoids testing bits which we have no business testing when no interrupt is pending. Patch against 2.2.17-pre5 attached. It hasn't been tested on a 3c590 (vortex series) since I put the OOM stuff in. I didn't actually address possible OOM problems with the vortex series, so the vortex code paths should not be affected. I'd appreciate it if you could test it on your 590. (Andreas: you too, please). --------------A028613A1132A2E773E9E93A Content-Type: text/plain; charset=us-ascii; name="3c59x.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="3c59x.patch" --- linux-2.2.17pre5/drivers/net/3c59x.c Fri Jun 23 01:15:43 2000 +++ linux-akpm/drivers/net/3c59x.c Sun Jun 25 02:06:07 2000 @@ -42,15 +42,20 @@ - In vortex_error, do_tx_reset and vortex_tx_timeout(Vortex): clear tbusy and force a BH rerun to better recover from errors. - 12Jun00 <2.2.16> andrewm + 24Jun00 <2.2.16> andrewm - Better handling of shared interrupts - Reset the transmitter in vortex_error() on both maxcollisions and Tx reclaim error + - Split the ISR into vortex_interrupt and boomerang_interrupt. This is + to fix once-and-for-all the dubious testing of vortex status bits on + boomerang/hurricane/cyclone/tornado NICs. + - Fixed crash under OOM during vortex_open() (Mark Hemment) + - Fix Rx cessation problem during OOM (help from Mark Hemment) - See http://www.uow.edu.au/~andrewm/linux/#3c59x-2.2 for more details. */ static char *version = -"3c59x.c:v0.99H 12Jun00 Donald Becker and others http://www.scyld.com/network/vortex.html\n"; +"3c59x.c:v0.99H 24Jun00 Donald Becker and others http://www.scyld.com/network/vortex.html\n"; /* "Knobs" that adjust features and parameters. */ /* Set the copy breakpoint for the copy-only-tiny-frames scheme. @@ -563,6 +568,7 @@ static int vortex_rx(struct device *dev); static int boomerang_rx(struct device *dev); static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void boomerang_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int vortex_close(struct device *dev); static void update_stats(long ioaddr, struct device *dev); static struct net_device_stats *vortex_get_stats(struct device *dev); @@ -570,6 +576,8 @@ static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd); +/* #define dev_alloc_skb dev_alloc_skb_debug */ + /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ /* Option count limit only -- unlimited interfaces are supported. */ #define MAX_UNITS 8 @@ -1033,12 +1041,13 @@ request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); /* The 3c59x-specific entries in the device structure. */ - dev->open = &vortex_open; - dev->hard_start_xmit = &vortex_start_xmit; - dev->stop = &vortex_close; - dev->get_stats = &vortex_get_stats; - dev->do_ioctl = &vortex_ioctl; - dev->set_multicast_list = &set_rx_mode; + dev->open = vortex_open; + dev->hard_start_xmit = vp->full_bus_master_tx ? + boomerang_start_xmit : vortex_start_xmit; + dev->stop = vortex_close; + dev->get_stats = vortex_get_stats; + dev->do_ioctl = vortex_ioctl; + dev->set_multicast_list = set_rx_mode; return dev; } @@ -1050,7 +1059,9 @@ long ioaddr = dev->base_addr; struct vortex_private *vp = (struct vortex_private *)dev->priv; unsigned int config; - int i; + int i, retval; + + MOD_INC_USE_COUNT; /* Before initializing select the active media port. */ EL3WINDOW(3); @@ -1072,12 +1083,6 @@ } else dev->if_port = vp->default_media; - init_timer(&vp->timer); - vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait); - vp->timer.data = (unsigned long)dev; - vp->timer.function = &vortex_timer; /* timer handler */ - add_timer(&vp->timer); - if (vortex_debug > 1) printk(KERN_DEBUG "%s: Initial media type %s.\n", dev->name, media_tbl[dev->if_port].name); @@ -1127,8 +1132,11 @@ outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); /* Use the now-standard shared IRQ implementation. */ - if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, dev->name, dev)) { - return -EAGAIN; + if ((retval = request_irq(dev->irq, vp->full_bus_master_tx ? + &boomerang_interrupt : &vortex_interrupt, + SA_SHIRQ, dev->name, dev))) { + printk(KERN_ERR "%s: Cannot allocate IRQ #%d\n", dev->name, dev->irq); + goto out; } if (vortex_debug > 1) { @@ -1193,12 +1201,20 @@ vp->rx_ring[i].addr = virt_to_bus(skb->data); #endif } + if (i != RX_RING_SIZE) { + int j; + for (j = 0; j < RX_RING_SIZE; j++) { + if (vp->rx_skbuff[j]) + DEV_FREE_SKB(vp->rx_skbuff[j]); + } + retval = -ENOMEM; + goto out_free_irq; + } /* Wrap the ring. */ vp->rx_ring[i-1].next = cpu_to_le32(virt_to_bus(&vp->rx_ring[0])); outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); } if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ - dev->hard_start_xmit = &boomerang_start_xmit; vp->cur_tx = vp->dirty_tx = 0; outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */ /* Clear the Tx ring. */ @@ -1232,9 +1248,18 @@ if (vp->cb_fn_base) /* The PCMCIA people are idiots. */ writel(0x8000, vp->cb_fn_base + 4); - MOD_INC_USE_COUNT; + init_timer(&vp->timer); + vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait); + vp->timer.data = (unsigned long)dev; + vp->timer.function = &vortex_timer; /* timer handler */ + add_timer(&vp->timer); return 0; + +out_free_irq: + free_irq(dev->irq, dev); +out: + return retval; } static void vortex_timer(unsigned long data) @@ -1372,7 +1397,10 @@ unsigned long flags; __save_flags(flags); __cli(); - vortex_interrupt(dev->irq, dev, 0); + if (vp->full_bus_master_tx) + boomerang_interrupt(dev->irq, dev, 0); + else + vortex_interrupt(dev->irq, dev, 0); __restore_flags(flags); } } @@ -1606,10 +1634,10 @@ int i; if (vortex_debug > 3) - printk(KERN_DEBUG "%s: Trying to send a packet, Tx index %d.\n", + printk(KERN_DEBUG "%s: Trying to send a boomerang packet, Tx index %d.\n", dev->name, vp->cur_tx); if (vp->tx_full) { - if (vortex_debug >0) + if (vortex_debug > 0) printk(KERN_WARNING "%s: Tx Ring full, refusing to send buffer.\n", dev->name); return 1; @@ -1623,7 +1651,7 @@ spin_lock_irqsave(&vp->lock, flags); outw(DownStall, ioaddr + EL3_CMD); /* Wait for the stall to complete. */ - for (i = 4000; i >= 0 ; i--) + for (i = 4000; i >= 0; i--) if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) break; prev_entry->next = cpu_to_le32(virt_to_bus(&vp->tx_ring[entry])); @@ -1649,6 +1677,12 @@ /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ + +/* + * This is the ISR for the vortex series chips. + * full_bus_master_tx == 0 && full_bus_master_rx == 0 + */ + static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct device *dev = dev_id; @@ -1660,8 +1694,11 @@ ioaddr = dev->base_addr; spin_lock(&vp->lock); status = inw(ioaddr + EL3_STATUS); - if ((status & IntLatch) == 0) + if ((status & IntLatch) == 0) { + if (vortex_debug > 5) + printk(KERN_DEBUG "%s: no vortex interrupt pending\n", dev->name); goto no_int; /* Happens during shared interrupts */ + } if (status & IntReq) { status |= vp->deferred; @@ -1669,19 +1706,16 @@ } if (vortex_debug > 4) - printk(KERN_DEBUG "%s: interrupt, status %4.4x, latency %d ticks.\n", + printk(KERN_DEBUG "%s: vortex_interrupt, status %4.4x, latency %d ticks.\n", 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); + if (status & RxComplete) vortex_rx(dev); - if (status & UpComplete) { - outw(AckIntr | UpComplete, ioaddr + EL3_CMD); - boomerang_rx(dev); - } if (status & TxAvailable) { if (vortex_debug > 5) @@ -1692,6 +1726,93 @@ mark_bh(NET_BH); } + if (status & DMADone) { + if (inw(ioaddr + Wn7_MasterStatus) & 0x1000) { + outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ + DEV_FREE_SKB(vp->tx_skb); /* Release the transfered buffer */ + if (inw(ioaddr + TxFree) > 1536) { + clear_bit(0, (void*)&dev->tbusy); + mark_bh(NET_BH); + } else /* Interrupt when FIFO has room for max-sized packet. */ + outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); + } + } + + /* Check for all uncommon interrupts at once. */ + if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq)) { + if (status == 0xffff) + break; + vortex_error(dev, status); + } + + if (--work_done < 0) { + printk(KERN_WARNING "%s: Too much work in interrupt, status " + "%4.4x.\n", dev->name, status); + /* Disable all pending interrupts. */ + do { + vp->deferred |= status; + outw(SetStatusEnb | (~vp->deferred & vp->status_enable), + ioaddr + EL3_CMD); + outw(AckIntr | (vp->deferred & 0x7ff), ioaddr + EL3_CMD); + } while ((status = inw(ioaddr + EL3_CMD)) & IntLatch); + /* The timer will reenable interrupts. */ + mod_timer(&vp->timer, RUN_AT(1)); + break; + } + + /* Acknowledge the IRQ. */ + outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); + } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); + + if (vortex_debug > 4) + printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n", + dev->name, status); + +no_int: + spin_unlock(&vp->lock); +} + +/* + * This is the ISR for the boomerang/cyclone/hurricane/tornado series chips. + * full_bus_master_tx == 1 && full_bus_master_rx == 1 + */ + +static void boomerang_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = dev_id; + struct vortex_private *vp = (struct vortex_private *)dev->priv; + long ioaddr; + int status; + int work_done = max_interrupt_work; + + ioaddr = dev->base_addr; + spin_lock(&vp->lock); + status = inw(ioaddr + EL3_STATUS); + if ((status & IntLatch) == 0) { + if (vortex_debug > 5) + printk(KERN_DEBUG "%s: no boomerang interrupt pending\n", dev->name); + goto no_int; /* Happens during shared interrupts */ + } + + if (status & IntReq) { + status |= vp->deferred; + vp->deferred = 0; + } + + if (vortex_debug > 4) + printk(KERN_DEBUG "%s: interrupt, status %04x, latency %d, cur_rx %d, dirty_rx %d\n", + dev->name, status, inb(ioaddr + Timer), vp->cur_rx, vp->dirty_rx); + + do { + if (vortex_debug > 5) + printk(KERN_DEBUG "%s: In interrupt loop, status %4.4x.\n", + dev->name, status); + + if (status & UpComplete) { + outw(AckIntr | UpComplete, ioaddr + EL3_CMD); + boomerang_rx(dev); + } + if (status & DownComplete) { unsigned int dirty_tx = vp->dirty_tx; @@ -1710,22 +1831,12 @@ vp->dirty_tx = dirty_tx; outw(AckIntr | DownComplete, ioaddr + EL3_CMD); if (vp->tx_full && (vp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) { - vp->tx_full= 0; + vp->tx_full = 0; clear_bit(0, (void*)&dev->tbusy); mark_bh(NET_BH); } } - if (status & DMADone) { - if (inw(ioaddr + Wn7_MasterStatus) & 0x1000) { - outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ - DEV_FREE_SKB(vp->tx_skb); /* Release the transfered buffer */ - if (inw(ioaddr + TxFree) > 1536) { - clear_bit(0, (void*)&dev->tbusy); - mark_bh(NET_BH); - } else /* Interrupt when FIFO has room for max-sized packet. */ - outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); - } - } + /* Check for all uncommon interrupts at once. */ if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq)) { if (status == 0xffff) @@ -1734,25 +1845,18 @@ } if (--work_done < 0) { - if ((status & (0x7fe - (UpComplete | DownComplete))) == 0) { - /* Just ack these and return. */ - outw(AckIntr | UpComplete | DownComplete, ioaddr + EL3_CMD); - } else { - printk(KERN_WARNING "%s: Too much work in interrupt, status " - "%4.4x.\n", dev->name, status); - /* Disable all pending interrupts. */ - do { - vp->deferred |= status; - outw(SetStatusEnb | (~vp->deferred & vp->status_enable), - ioaddr + EL3_CMD); - outw(AckIntr | (vp->deferred & 0x7ff), ioaddr + EL3_CMD); - } while ((status = inw(ioaddr + EL3_CMD)) & IntLatch); - /* The timer will reenable interrupts. */ - del_timer(&vp->timer); - vp->timer.expires = RUN_AT(1); - add_timer(&vp->timer); - break; - } + printk(KERN_WARNING "%s: Too much work in interrupt, status " + "%4.4x.\n", dev->name, status); + /* Disable all pending interrupts. */ + do { + vp->deferred |= status; + outw(SetStatusEnb | (~vp->deferred & vp->status_enable), + ioaddr + EL3_CMD); + outw(AckIntr | (vp->deferred & 0x7ff), ioaddr + EL3_CMD); + } while ((status = inw(ioaddr + EL3_CMD)) & IntLatch); + /* The timer will reenable interrupts. */ + mod_timer(&vp->timer, RUN_AT(1)); + break; } /* Acknowledge the IRQ. */ @@ -1760,7 +1864,15 @@ if (vp->cb_fn_base) /* The PCMCIA people are idiots. */ writel(0x8000, vp->cb_fn_base + 4); - } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); + } while ((status = inw(ioaddr + EL3_STATUS)) & IntLatch); + + /* + * If we have totally run out to rx skb's due to persistent OOM, + * we can use the Tx interrupt to retry the allocation. Dirty + * but expedient + */ + if ((vp->cur_rx - vp->dirty_rx) == RX_RING_SIZE) + boomerang_rx(dev); if (vortex_debug > 4) printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n", --------------A028613A1132A2E773E9E93A--