[PATCH] Fix SMP support on 3c527 net driver, take 2

From: Felipe W Damasio
Date: Tue Sep 02 2003 - 16:47:32 EST


Hi Richard,

This is second version of the patch (against 2.6-test4) to fix SMP support on the 3c527 net driver, by removing cli/sti calls and replacing them with local locks.

This one includes Manfred's idea of using wait_event instead of sleep_on, and releasing the lock to prevent the driver from going bezerk..it also uses spinlocks, since on some situations we are in bottom half context.

Compiles fine, but I don't have the hardware to test it.

Could you please see if this works for you?

Manfred, could you take a look at this too?

Comments are welcome so can get this fix right,

Thanks.

Felipe --- linux-2.6.0-test4/drivers/net/3c527.c Fri Aug 22 20:56:34 2003
+++ linux-2.6.0-test4-fwd/drivers/net/3c527.c Tue Sep 2 16:21:47 2003
@@ -17,8 +17,8 @@
*/

#define DRV_NAME "3c527"
-#define DRV_VERSION "0.6a"
-#define DRV_RELDATE "2001/11/17"
+#define DRV_VERSION "0.6b"
+#define DRV_RELDATE "2003/09/2"

static const char *version =
DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Richard Proctor (rnp@xxxxxxxxxxxxx)\n";
@@ -100,6 +100,7 @@
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/ethtool.h>
+#include <linux/spinlock.h>

#include <asm/uaccess.h>
#include <asm/system.h>
@@ -157,11 +158,11 @@
volatile struct mc32_mailbox *rx_box;
volatile struct mc32_mailbox *tx_box;
volatile struct mc32_mailbox *exec_box;
- volatile struct mc32_stats *stats; /* Start of on-card statistics */
- u16 tx_chain; /* Transmit list start offset */
+ volatile struct mc32_stats *stats; /* Start of on-card statistics */
+ u16 tx_chain; /* Transmit list start offset */
u16 rx_chain; /* Receive list start offset */
- u16 tx_len; /* Transmit list count */
- u16 rx_len; /* Receive list count */
+ u16 tx_len; /* Transmit list count */
+ u16 rx_len; /* Receive list count */

u32 base;
u16 exec_pending;
@@ -179,6 +180,7 @@
u16 tx_ring_head; /* index to tx en-queue end */

u16 rx_ring_tail; /* index to rx de-queue end */
+ spinlock_t lock;
};

/* The station (ethernet) address prefix, used for a sanity check. */
@@ -197,7 +199,6 @@
{ 0x0000, NULL }
};

-
/* Macros for ring index manipulations */
static inline u16 next_rx(u16 rx) { return (rx+1)&(RX_RING_LEN-1); };
static inline u16 prev_rx(u16 rx) { return (rx-1)&(RX_RING_LEN-1); };
@@ -536,7 +537,7 @@
* command register. This tells us nothing about the completion
* status of any pending commands and takes very little time at all.
*/
-
+
static void mc32_ready_poll(struct net_device *dev)
{
int ioaddr = dev->base_addr;
@@ -579,6 +580,24 @@
return 0;
}

+/**
+ * wait_pending - sleep until a field reaches a certain value
+ * @lp: m32_local structure describing the target card
+ *
+ * The caller must acquire lp->lock before calling this function, it
+ * temporarily drops the lock when it sleeps (SMP-safe).
+ */
+
+static inline void wait_pending(struct mc32_local *lp)
+{
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait(&lp->event, &wait, TASK_UNINTERRUPTIBLE);
+ spin_unlock_irq(&lp->lock);
+ schedule();
+ finish_wait(&lp->event, &wait);
+ spin_lock_irq(&lp->lock);
+}

/**
* mc32_command - send a command and sleep until completion
@@ -619,14 +638,12 @@
int ret = 0;

/*
- * Wait for a command
+ * Wait until there are no more pending commands
*/

- save_flags(flags);
- cli();
-
- while(lp->exec_pending)
- sleep_on(&lp->event);
+ spin_lock_irqsave(&lp->lock, flags);
+ while (lp->exec_pending)
+ wait_pending(lp);

/*
* Issue mine
@@ -634,7 +651,7 @@

lp->exec_pending=1;

- restore_flags(flags);
+ spin_unlock_irqrestore(&lp->lock, flags);

lp->exec_box->mbox=0;
lp->exec_box->mbox=cmd;
@@ -645,13 +662,11 @@
while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
outb(1<<6, ioaddr+HOST_CMD);

- save_flags(flags);
- cli();
-
- while(lp->exec_pending!=2)
- sleep_on(&lp->event);
+ spin_lock_irqsave(&lp->lock, flags);
+ while (lp->exec_pending != 2)
+ wait_pending (lp);
lp->exec_pending=0;
- restore_flags(flags);
+ spin_unlock_irqrestore(&lp->lock, flags);

if(lp->exec_box->mbox&(1<<13))
ret = -1;
@@ -735,15 +750,14 @@
outb(HOST_CMD_SUSPND_RX, ioaddr+HOST_CMD);
mc32_ready_poll(dev);
outb(HOST_CMD_SUSPND_TX, ioaddr+HOST_CMD);
-
- save_flags(flags);
- cli();
-
+
+ spin_lock_irqsave (&lp->lock, flags);
+
while(lp->xceiver_state!=HALTED)
sleep_on(&lp->event);
-
- restore_flags(flags);
-}
+
+ spin_unlock_irqrestore (&lp->lock, flags);
+}


/**
@@ -1062,12 +1076,11 @@

netif_stop_queue(dev);

- save_flags(flags);
- cli();
-
+ spin_lock_irqsave (&lp->lock, flags);
+
if(atomic_read(&lp->tx_count)==0)
{
- restore_flags(flags);
+ spin_unlock_irqrestore (&lp->lock, flags);
return 1;
}

@@ -1098,7 +1111,7 @@

p->control &= ~CONTROL_EOL; /* Clear EOL on p */
out:
- restore_flags(flags);
+ spin_unlock_irqrestore (&lp->lock, flags);

netif_wake_queue(dev);
return 0;