diff options
Diffstat (limited to 'drivers/net/sunlance.c')
-rw-r--r-- | drivers/net/sunlance.c | 118 |
1 files changed, 75 insertions, 43 deletions
diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index 6901c7103..3485a2a84 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -1,4 +1,4 @@ -/* $Id: sunlance.c,v 1.81 1998/08/10 09:08:23 jj Exp $ +/* $Id: sunlance.c,v 1.85 1999/03/21 05:22:05 davem Exp $ * lance.c: Linux/Sparc/Lance driver * * Written 1995, 1996 by Miguel de Icaza @@ -56,12 +56,16 @@ * * 1.11: * 12/27/97: Added sun4d support. (jj@sunsite.mff.cuni.cz) + * + * 1.12: + * 11/3/99: Fixed SMP race in lance_start_xmit found by davem. + * Anton Blanchard (anton@progsoc.uts.edu.au) */ #undef DEBUG_DRIVER static char *version = - "sunlance.c:v1.11 27/Dec/97 Miguel de Icaza (miguel@nuclecu.unam.mx)\n"; + "sunlance.c:v1.12 11/Mar/99 Miguel de Icaza (miguel@nuclecu.unam.mx)\n"; static char *lancestr = "LANCE"; static char *lancedma = "LANCE DMA"; @@ -252,6 +256,7 @@ struct lance_private { struct device *dev; /* Backpointer */ struct lance_private *next_module; struct linux_sbus *sbus; + struct timer_list multicast_timer; }; #define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ @@ -326,8 +331,6 @@ static void lance_init_ring (struct device *dev) lp->rx_new = lp->tx_new = 0; lp->rx_old = lp->tx_old = 0; - ib->mode = 0; - /* Copy the ethernet address to the lance init block * Note that on the sparc you need to swap the ethernet address. * Note also we want the CPU ptr of the init_block here. @@ -384,10 +387,6 @@ static void lance_init_ring (struct device *dev) ib->tx_ptr = leptr; if (ZERO) printk ("TX ptr: %8.8x\n", leptr); - - /* Clear the multicast filter */ - ib->filter [0] = 0; - ib->filter [1] = 0; } static int init_restart_lance (struct lance_private *lp) @@ -668,6 +667,7 @@ static int lance_open (struct device *dev) { struct lance_private *lp = (struct lance_private *)dev->priv; volatile struct lance_regs *ll = lp->ll; + volatile struct lance_init_block *ib = lp->init_block; int status = 0; last_dev = dev; @@ -686,6 +686,16 @@ static int lance_open (struct device *dev) if (lp->ledma) lp->ledma->regs->dma_test = ((__u32) lp->init_block_dvma) & 0xff000000; + /* Set mode and clear multicast filter only at device open, + so that lance_init_ring() called at any error will not + forget multicast filters. + + BTW it is common bug in all lance drivers! --ANK + */ + ib->mode = 0; + ib->filter [0] = 0; + ib->filter [1] = 0; + lance_init_ring (dev); load_csrs (lp); @@ -742,6 +752,7 @@ static int lance_close (struct device *dev) dev->start = 0; dev->tbusy = 1; + del_timer(&lp->multicast_timer); /* Stop the card */ ll->rap = LE_CSR0; @@ -791,27 +802,19 @@ static int lance_start_xmit (struct sk_buff *skb, struct device *dev) volatile unsigned long flush; unsigned long flags; int entry, skblen, len; - int status = 0; - static int outs; - /* Transmitter timeout, serious problems */ - if (dev->tbusy) { + if (test_and_set_bit (0, (void *) &dev->tbusy) != 0) { int tickssofar = jiffies - dev->trans_start; - - if (tickssofar < 100) { - status = -1; - } else { - printk ("%s: transmit timed out, status %04x, reset\n", - dev->name, ll->rdp); - lance_reset (dev); - } - return status; - } - /* Block a timer-based transmit from overlapping. */ - if (test_and_set_bit (0, (void *) &dev->tbusy) != 0) { - printk ("Transmitter access conflict.\n"); - return -1; + if (tickssofar < 100) + return 1; + + printk ("%s: transmit timed out, status %04x, reset\n", + dev->name, ll->rdp); + lp->stats.tx_errors++; + lance_reset (dev); + + return 1; } skblen = skb->len; @@ -820,13 +823,13 @@ static int lance_start_xmit (struct sk_buff *skb, struct device *dev) if (!TX_BUFFS_AVAIL) { restore_flags(flags); - return -1; + return 1; } len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen; - + lp->stats.tx_bytes += len; - + entry = lp->tx_new & TX_RING_MOD_MASK; ib->btx_ring [entry].length = (-len) | 0xf000; ib->btx_ring [entry].misc = 0; @@ -842,7 +845,6 @@ static int lance_start_xmit (struct sk_buff *skb, struct device *dev) ib->btx_ring [entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN); lp->tx_new = (lp->tx_new+1) & TX_RING_MOD_MASK; - outs++; /* Kick the lance: transmit now */ ll->rdp = LE_C0_INEA | LE_C0_TDMD; dev->trans_start = jiffies; @@ -857,7 +859,7 @@ static int lance_start_xmit (struct sk_buff *skb, struct device *dev) flush = ll->rdp; restore_flags(flags); - return status; + return 0; } static struct net_device_stats *lance_get_stats (struct device *dev) @@ -879,7 +881,7 @@ static void lance_load_multicast (struct device *dev) u32 crc, poly = CRC_POLYNOMIAL_LE; /* set all multicast bits */ - if (dev->flags & IFF_ALLMULTI){ + if (dev->flags & IFF_ALLMULTI) { ib->filter [0] = 0xffffffff; ib->filter [1] = 0xffffffff; return; @@ -889,31 +891,29 @@ static void lance_load_multicast (struct device *dev) ib->filter [1] = 0; /* Add addresses */ - for (i = 0; i < dev->mc_count; i++){ + for (i = 0; i < dev->mc_count; i++) { addrs = dmi->dmi_addr; dmi = dmi->next; /* multicast address? */ if (!(*addrs & 1)) continue; - + crc = 0xffffffff; - for (byte = 0; byte < 6; byte++) + for (byte = 0; byte < 6; byte++) { for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) { int test; test = ((bit ^ crc) & 0x01); crc >>= 1; - if (test) { + if (test) crc = crc ^ poly; - } } - + } crc = crc >> 26; mcast_table [crc >> 4] |= 1 << (crc & 0xf); } - return; } static void lance_set_multicast (struct device *dev) @@ -922,12 +922,33 @@ static void lance_set_multicast (struct device *dev) volatile struct lance_init_block *ib = lp->init_block; volatile struct lance_regs *ll = lp->ll; - while (dev->tbusy) - schedule(); + if (!dev->start) + return; + + if (dev->tbusy) { + mod_timer(&lp->multicast_timer, jiffies + 2); + return; + } + /* This CANNOT be correct. Chip is running + and dev->tbusy may change any moment. + It is useless to set it. + + Generally, usage of dev->tbusy in this driver is completely + wrong. + + I protected calls to this function + with start_bh_atomic, so that set_multicast_list + and hard_start_xmit are serialized now by top level. --ANK + + The same is true about a2065. + */ set_bit (0, (void *) &dev->tbusy); - while (lp->tx_old != lp->tx_new) - schedule(); + if (lp->tx_old != lp->tx_new) { + mod_timer(&lp->multicast_timer, jiffies + 4); + dev->tbusy = 0; + return; + } ll->rap = LE_CSR0; ll->rdp = LE_C0_STOP; @@ -942,6 +963,7 @@ static void lance_set_multicast (struct device *dev) load_csrs (lp); init_restart_lance (lp); dev->tbusy = 0; + mark_bh(NET_BH); } __initfunc(static int @@ -1101,6 +1123,16 @@ no_link_test: dev->dma = 0; ether_setup (dev); + /* We cannot sleep if the chip is busy during a + * multicast list update event, because such events + * can occur from interrupts (ex. IPv6). So we + * use a timer to try again later when necessary. -DaveM + */ + init_timer(&lp->multicast_timer); + lp->multicast_timer.data = (unsigned long) dev; + lp->multicast_timer.function = + (void (*)(unsigned long)) &lance_set_multicast; + #ifdef MODULE dev->ifindex = dev_new_index(); lp->next_module = root_lance_dev; |