patch-2.2.4 linux/drivers/net/sunlance.c
Next file: linux/drivers/net/sunqe.c
Previous file: linux/drivers/net/sunhme.h
Back to the patch index
Back to the overall index
- Lines: 266
- Date:
Sun Mar 21 07:22:00 1999
- Orig file:
v2.2.3/linux/drivers/net/sunlance.c
- Orig date:
Sat Sep 5 16:46:40 1998
diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunlance.c linux/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 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 @@
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 @@
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 @@
{
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 @@
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 @@
dev->start = 0;
dev->tbusy = 1;
+ del_timer(&lp->multicast_timer);
/* Stop the card */
ll->rap = LE_CSR0;
@@ -791,27 +802,19 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
load_csrs (lp);
init_restart_lance (lp);
dev->tbusy = 0;
+ mark_bh(NET_BH);
}
__initfunc(static int
@@ -1100,6 +1122,16 @@
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();
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)