lkml.org 
[lkml]   [2003]   [Sep]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: [PATCH] Fix SMP support on 3c527 net driver, take 2


Alan Cox wrote:
> On Iau, 2003-09-04 at 13:52, Felipe W Damasio wrote:
>
>> But I'm not sure on how we should go here: We go get the hold lock
>>right after 'status=inb(ioaddr+HOST_CMD)', so we can treat the
>>interrupt safely.
>>
>> Although, there are some places that we do "wake_up (lp->event)",
>
>
> wake_up will not sleep, its safe from interrupts and with locks held.

So I think we can use something like the attached patch, then..which
get the lock before the while loop on mc32_interrupt and releases it
after processing the interrupts.

Richard, could you please test it?

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 Thu Sep 4 10:24:40 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@netlink.co.nz)\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();
+ spin_lock_irq(&lp->lock);
+ finish_wait(&lp->event, &wait);
+}

/**
* 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);
-}
+ wait_pending (lp);
+
+ 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;
@@ -1354,6 +1367,7 @@
int ioaddr, status, boguscount = 0;
int rx_event = 0;
int tx_event = 0;
+ unsigned long flags;

if (dev == NULL) {
printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
@@ -1365,6 +1379,8 @@

/* See whats cooking */

+ spin_lock_irqsave(&lp->lock, flags);
+
while((inb(ioaddr+HOST_STATUS)&HOST_STATUS_CWR) && boguscount++<2000)
{
status=inb(ioaddr+HOST_CMD);
@@ -1386,7 +1402,7 @@
case 3: /* Halt */
case 4: /* Abort */
lp->xceiver_state |= TX_HALTED;
- wake_up(&lp->event);
+ wake_up(&lp->event);
break;
default:
printk("%s: strange tx ack %d\n", dev->name, status&7);
@@ -1435,8 +1451,8 @@

if (lp->mc_reload_wait)
mc32_reset_multicast_list(dev);
- else
- wake_up(&lp->event);
+ else
+ wake_up(&lp->event);
}
}
if(status&2)
@@ -1450,7 +1466,6 @@
}
}

-
/*
* Process the transmit and receive rings
*/
@@ -1461,6 +1476,8 @@
if(rx_event)
mc32_rx_ring(dev);

+ spin_unlock_irqrestore(&lp->lock, flags);
+
return IRQ_HANDLED;
}

@@ -1492,6 +1509,8 @@
struct mc32_local *lp = (struct mc32_local *)dev->priv;

int ioaddr = dev->base_addr;
+ unsigned long flags;
+
u8 regs;
u16 one=1;

@@ -1509,9 +1528,11 @@
mc32_halt_transceiver(dev);

/* Catch any waiting commands */
-
+
+ spin_lock_irqsave(&lp->lock, flags);
while(lp->exec_pending==1)
- sleep_on(&lp->event);
+ wait_pending(lp);
+ spin_unlock_irqrestore(&lp->lock, flags);

/* Ok the card is now stopping */
\
 
 \ /
  Last update: 2005-03-22 13:48    [from the cache]
©2003-2011 Jasper Spaans. Advertise on this site