patch-2.3.43 linux/drivers/net/sunqe.c
Next file: linux/drivers/net/sunqe.h
Previous file: linux/drivers/net/sunlance.c
Back to the patch index
Back to the overall index
- Lines: 230
- Date:
Thu Feb 10 12:29:58 2000
- Orig file:
v2.3.42/linux/drivers/net/sunqe.c
- Orig date:
Tue Feb 1 01:35:44 2000
diff -u --recursive --new-file v2.3.42/linux/drivers/net/sunqe.c linux/drivers/net/sunqe.c
@@ -1,4 +1,4 @@
-/* $Id: sunqe.c,v 1.41 2000/01/28 13:42:30 jj Exp $
+/* $Id: sunqe.c,v 1.43 2000/02/09 21:11:19 davem Exp $
* sunqe.c: Sparc QuadEthernet 10baseT SBUS card driver.
* Once again I am out to prove that every ethernet
* controller out there can be most efficiently programmed
@@ -455,6 +455,8 @@
printk(KERN_NOTICE "%s: Memory squeeze, deferring packet.\n", qep->dev->name);
}
+static void qe_tx_reclaim(struct sunqe *qep);
+
/* Interrupts for all QE's get filtered out via the QEC master controller,
* so we just run through each qe and check to see who is signaling
* and thus needs to be serviced.
@@ -470,11 +472,8 @@
while (channel < 4) {
if (qec_status & 0xf) {
struct sunqe *qep = qecp->qes[channel];
- struct net_device *dev = qep->dev;
u32 qe_status;
- dev->interrupt = 1;
-
qe_status = sbus_readl(qep->qcregs + CREG_STAT);
if (qe_status & CREG_STAT_ERRORS) {
if (qe_is_bolixed(qep, qe_status))
@@ -482,8 +481,20 @@
}
if (qe_status & CREG_STAT_RXIRQ)
qe_rx(qep);
+ if (test_bit(LINK_STATE_XOFF, &qep->dev->state) &&
+ (qe_status & CREG_STAT_TXIRQ)) {
+ spin_lock(&qep->lock);
+ qe_tx_reclaim(qep);
+ if (TX_BUFFS_AVAIL(qep) > 0) {
+ /* Wake net queue and return to
+ * lazy tx reclaim.
+ */
+ netif_wake_queue(qep->dev);
+ sbus_writel(1, qep->qcregs + CREG_TIMASK);
+ }
+ spin_unlock(&qep->lock);
+ }
next:
- dev->interrupt = 0;
}
qec_status >>= 4;
channel++;
@@ -514,11 +525,12 @@
return 0;
}
-/* Reclaim TX'd frames from the ring. */
+/* Reclaim TX'd frames from the ring. This must always run under
+ * the IRQ protected qep->lock.
+ */
static void qe_tx_reclaim(struct sunqe *qep)
{
struct qe_txd *txbase = &qep->qe_block->qe_txd[0];
- struct net_device *dev = qep->dev;
int elem = qep->tx_old;
while (elem != qep->tx_new) {
@@ -529,11 +541,31 @@
elem = NEXT_TX(elem);
}
qep->tx_old = elem;
+}
- if (dev->tbusy && (TX_BUFFS_AVAIL(qep) > 0)) {
- dev->tbusy = 0;
- mark_bh(NET_BH);
- }
+static void qe_tx_timeout(struct net_device *dev)
+{
+ struct sunqe *qep = (struct sunqe *) dev->priv;
+ int tx_full;
+
+ spin_lock_irq(&qep->lock);
+
+ /* Try to reclaim, if that frees up some tx
+ * entries, we're fine.
+ */
+ qe_tx_reclaim(qep);
+ tx_full = TX_BUFFS_AVAIL(qep) <= 0;
+
+ spin_unlock_irq(&qep->lock);
+
+ if (! tx_full)
+ goto out;
+
+ printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
+ qe_init(qep, 1);
+
+out:
+ netif_wake_queue(dev);
}
/* Get a packet queued to go onto the wire. */
@@ -545,19 +577,9 @@
unsigned char *txbuf;
int len, entry;
- qe_tx_reclaim(qep);
-
- if (test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
- long tickssofar = jiffies - dev->trans_start;
+ spin_lock_irq(&qep->lock);
- if (tickssofar >= 40) {
- printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
- qe_init(qep, 1);
- dev->tbusy = 0;
- dev->trans_start = jiffies;
- }
- return 1;
- }
+ qe_tx_reclaim(qep);
len = skb->len;
entry = qep->tx_new;
@@ -583,10 +605,18 @@
qep->net_stats.tx_packets++;
qep->net_stats.tx_bytes += len;
- dev_kfree_skb(skb);
+ if (TX_BUFFS_AVAIL(qep) <= 0) {
+ /* Halt the net queue and enable tx interrupts.
+ * When the tx queue empties the tx irq handler
+ * will wake up the queue and return us back to
+ * the lazy tx reclaim scheme.
+ */
+ netif_stop_queue(dev);
+ sbus_writel(0, qep->qcregs + CREG_TIMASK);
+ }
+ spin_unlock_irq(&qep->lock);
- if (TX_BUFFS_AVAIL(qep))
- dev->tbusy = 0;
+ dev_kfree_skb(skb);
return 0;
}
@@ -611,7 +641,7 @@
u32 crc, poly = CRC_POLYNOMIAL_LE;
/* Lock out others. */
- set_bit(0, (void *) &dev->tbusy);
+ netif_stop_queue(dev);
if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
sbus_writeb(MREGS_IACONFIG_ACHNGE | MREGS_IACONFIG_LARESET,
@@ -673,7 +703,7 @@
sbus_writeb(qep->mconfig, qep->mregs + MREGS_MCONFIG);
/* Let us get going again. */
- dev->tbusy = 0;
+ netif_wake_queue(dev);
}
/* This is only called once at boot time for each card probed. */
@@ -722,6 +752,7 @@
qe_devs[0] = dev;
qeps[0] = (struct sunqe *) dev->priv;
qeps[0]->channel = 0;
+ spin_lock_init(&qeps[0]->lock);
for (j = 0; j < 6; j++)
qe_devs[0]->dev_addr[j] = idprom->id_ethaddr[j];
@@ -857,6 +888,8 @@
qe_devs[i]->hard_start_xmit = qe_start_xmit;
qe_devs[i]->get_stats = qe_get_stats;
qe_devs[i]->set_multicast_list = qe_set_multicast;
+ qe_devs[i]->tx_timeout = qe_tx_timeout;
+ qe_devs[i]->watchdog_timeo = 5*HZ;
qe_devs[i]->irq = sdev->irqs[0];
qe_devs[i]->dma = 0;
ether_setup(qe_devs[i]);
@@ -953,7 +986,7 @@
return 1;
}
-int __init qec_probe(void)
+static int __init qec_probe(void)
{
struct net_device *dev = NULL;
struct sbus_bus *bus;
@@ -961,6 +994,10 @@
static int called = 0;
int cards = 0, v;
+#ifdef MODULE
+ root_qec_dev = NULL;
+#endif
+
if (called)
return ENODEV;
called++;
@@ -982,18 +1019,9 @@
return 0;
}
-#ifdef MODULE
-
-int
-init_module(void)
-{
- root_qec_dev = NULL;
- return qec_probe();
-}
-
-void
-cleanup_module(void)
+static void __exit qec_cleanup(void)
{
+#ifdef MODULE
struct sunqec *next_qec;
int i;
@@ -1021,6 +1049,8 @@
kfree(root_qec_dev);
root_qec_dev = next_qec;
}
+#endif /* MODULE */
}
-#endif /* MODULE */
+module_init(qec_probe);
+module_exit(qec_cleanup);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)