diff options
author | Mike Shaver <shaver@ingenia.com> | 1997-08-20 18:18:52 +0000 |
---|---|---|
committer | Mike Shaver <shaver@ingenia.com> | 1997-08-20 18:18:52 +0000 |
commit | 8e3dfa33f12d5c4195a985ea6b49f41b1ae7d5a5 (patch) | |
tree | 15a94aea4d1baf10185e478e80b7d39f85038742 /drivers | |
parent | fc3482fb16dbbcdfdf55b552f1eec635e93fa0d0 (diff) |
Mark Salter's patch to fix the excessive eth0 timeout bug.
Mark's description of the problem:
This patch seems to have fixed the excessive ethernet timeouts
I've been seeing. The problem was a race condition when queueing
another packet for transmisson. The code in sgiseeq_start_xmit()
would try to start another transmission from the head of the
transmit queue if the HPC was inactive. Because interrupts are
off at this time, it was possible that that packet had been
transmitted but the queue head had not been advanced by an
interrupt service. This resulted in a duplicate packet being
sent over the wire and when interrupts were reenabled, the
normal ack'ing of the original transmission of that packet
would put the HPC into a wedged state which eventually led to
a timeout and reset.
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/sgiseeq.c | 48 |
1 files changed, 26 insertions, 22 deletions
diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c index bf85e014a..fe8153561 100644 --- a/drivers/net/sgiseeq.c +++ b/drivers/net/sgiseeq.c @@ -1,4 +1,4 @@ -/* $Id: sgiseeq.c,v 1.17 1996/08/16 04:43:32 dm Exp $ +/* $Id: sgiseeq.c,v 1.1.1.1 1997/06/01 03:17:20 ralf Exp $ * sgiseeq.c: Seeq8003 ethernet driver for SGI machines. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) @@ -351,6 +351,25 @@ static inline void tx_maybe_reset_collisions(struct sgiseeq_private *sp, } } +static inline void kick_tx(struct sgiseeq_tx_desc *td, + volatile struct hpc3_ethregs *hregs) +{ + /* If the HPC aint doin nothin, and there are more packets + * with ETXD cleared and XIU set we must make very certain + * that we restart the HPC else we risk locking up the + * adapter. The following code is only safe iff the HPCDMA + * is not active! + */ + while((td->tdma.cntinfo & (HPCDMA_XIU | HPCDMA_ETXD)) == + (HPCDMA_XIU | HPCDMA_ETXD)) + td = (struct sgiseeq_tx_desc *) + KSEG1ADDR(td->tdma.pnext); + if(td->tdma.cntinfo & HPCDMA_XIU) { + hregs->tx_ndptr = PHYSADDR(td); + hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE; + } +} + static inline void sgiseeq_tx(struct device *dev, struct sgiseeq_private *sp, volatile struct hpc3_ethregs *hregs, volatile struct sgiseeq_regs *sregs) @@ -371,22 +390,8 @@ static inline void sgiseeq_tx(struct device *dev, struct sgiseeq_private *sp, if(status & SEEQ_TSTAT_LCLS) sp->stats.collisions++; } - /* If the HPC aint doin nothin, and there are more packets - * with ETXD cleared and XIU set we must make very certain - * that we restart the HPC else we risk locking up the - * adapter. The following read of tx_ndptr is only safe - * iff the HPCDMA is not active! - */ - td = (struct sgiseeq_tx_desc *) - KSEG1ADDR(((hregs->tx_ndptr) & ~0xf)); - while((td->tdma.cntinfo & (HPCDMA_XIU | HPCDMA_ETXD)) == - (HPCDMA_XIU | HPCDMA_ETXD)) - td = (struct sgiseeq_tx_desc *) - KSEG1ADDR(td->tdma.pnext); - if(td->tdma.cntinfo & HPCDMA_XIU) { - hregs->tx_ndptr = PHYSADDR(td); - hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE; - } + kick_tx((struct sgiseeq_tx_desc *)KSEG1ADDR((hregs->tx_ndptr & ~0xf)), + hregs); } /* Ack 'em... */ @@ -515,7 +520,7 @@ static inline int verify_tx(struct sgiseeq_private *sp, } if(skb->len <= 0) { - printk("%s: skb len is %ld\n", dev->name, skb->len); + printk("%s: skb len is %d\n", dev->name, skb->len); return -1; } /* Are we getting in someone else's way? */ @@ -575,10 +580,9 @@ static int sgiseeq_start_xmit(struct sk_buff *skb, struct device *dev) sp->tx_new = NEXT_TX(sp->tx_new); /* Advance. */ /* Maybe kick the HPC back into motion. */ - if(!(hregs->tx_ctrl & HPC3_ETXCTRL_ACTIVE)) { - hregs->tx_ndptr = PHYSADDR(&sp->srings.tx_desc[sp->tx_old]); - hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE; - } + if(!(hregs->tx_ctrl & HPC3_ETXCTRL_ACTIVE)) + kick_tx(&sp->srings.tx_desc[sp->tx_old], hregs); + dev->trans_start = jiffies; dev_kfree_skb(skb, FREE_WRITE); |