diff mbox

[RFC] smsc911x: protect INT_EN read-modify-write with spinlock

Message ID 1232460114-13469-1-git-send-email-steve.glendinning@smsc.com
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

Steve Glendinning Jan. 20, 2009, 2:01 p.m. UTC
This patch adds a new spinlock to protect read-modify-writes to the
INT_EN register when enabling and disabling interrupt sources.

I haven't actually seen any devices lock up, but I think there's a
possibility and I'd like to eliminate it.

Should I add this new spinlock, or should I extend the mac_lock
to also cover these sections (renaming it to simply "lock")?

Signed-off-by: Steve Glendinning <steve.glendinning@smsc.com>
---
 drivers/net/smsc911x.c |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

Comments

David Miller Jan. 26, 2009, 1:47 a.m. UTC | #1
From: Steve Glendinning <steve.glendinning@smsc.com>
Date: Tue, 20 Jan 2009 14:01:54 +0000

> This patch adds a new spinlock to protect read-modify-writes to the
> INT_EN register when enabling and disabling interrupt sources.
> 
> I haven't actually seen any devices lock up, but I think there's a
> possibility and I'd like to eliminate it.
> 
> Should I add this new spinlock, or should I extend the mac_lock
> to also cover these sections (renaming it to simply "lock")?
> 
> Signed-off-by: Steve Glendinning <steve.glendinning@smsc.com>

If you didn't have all of these RX multicast workaround
cases, things would be much easier.

The POLL and normal interrupt paths are already completely atomic for
you already.  And since POLL cannot happen until open() completes
that path would be safe too.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Steve Sakoman Jan. 26, 2009, 4:43 a.m. UTC | #2
On Tue, Jan 20, 2009 at 6:01 AM, Steve Glendinning
<steve.glendinning@smsc.com> wrote:
> This patch adds a new spinlock to protect read-modify-writes to the
> INT_EN register when enabling and disabling interrupt sources.
>
> I haven't actually seen any devices lock up, but I think there's a
> possibility and I'd like to eliminate it.

I'm attempting to use this driver on a new Gumstix Overo daughtercard
that uses an SMCS9221 and have been having some interrupt issues that
may or may not be related.

I am using the version in linux-omap 2.6.29-rc2.  Here's what I see at boot:

smsc911x: Driver version 2008-10-21.
eth%d: smsc911x_init: Driver Parameters:
eth%d: smsc911x_init: LAN base: 0xD086E000
eth%d: smsc911x_init: IRQ: 336
eth%d: smsc911x_init: PHY will be autodetected.
eth%d: smsc911x_init: BYTE_TEST: 0x87654321
eth%d: smsc911x_init: LAN911x identified, idrev: 0x92210000, generation: 4
eth0: smsc911x_drv_probe: Network interface: "eth0"
eth0: smsc911x_mii_init: External PHY is not supported, using internal PHY
smsc911x-mdio: probed
eth0: smsc911x_mii_probe: PHY 1: addr 1, phy_id 0x0007C0C3
eth0: attached PHY driver [SMSC LAN8700] (mii_bus:phy_addr=ffffffff:01, irq=-1)
eth0: smsc911x_phy_check_loopbackpkt: Successfully verified loopback packet
eth0: smsc911x_mii_probe: Passed Loop Back Test
eth0: smsc911x_mii_probe: phy initialised succesfully
eth0: smsc911x_drv_probe: MAC Address is set to random_ether_addr
net eth0: MAC Address: ae:b1:a7:f7:89:c2
<snip>
Configuring network interfaces...
eth0: smsc911x_open: irq polarity: active low
eth0: smsc911x_open: irq type: open drain
eth0: smsc911x_open: Testing irq handler using IRQ 336
net eth0: ISR failed signaling test (IRQ 336)

I have a scope probe hanging on the IRQ signal.  I can see that IRQ is
high at power on, then I see a flurry of interrupts,  then at around
the time the "testing irq" message comes out I see IRQ go low and
remain low.  This behavior is 100% reproducible.

I tried with and without this patch, same behavior.

Any ideas what might be going wrong?

Steve
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Steve Glendinning Jan. 26, 2009, 9:56 a.m. UTC | #3
David Miller <davem@davemloft.net> wrote on 26/01/2009 01:47:36:

> From: Steve Glendinning <steve.glendinning@smsc.com>
> Date: Tue, 20 Jan 2009 14:01:54 +0000
> 
> > This patch adds a new spinlock to protect read-modify-writes to the
> > INT_EN register when enabling and disabling interrupt sources.
> > 
> > I haven't actually seen any devices lock up, but I think there's a
> > possibility and I'd like to eliminate it.
> > 
> > Should I add this new spinlock, or should I extend the mac_lock
> > to also cover these sections (renaming it to simply "lock")?
> > 
> > Signed-off-by: Steve Glendinning <steve.glendinning@smsc.com>
> 
> If you didn't have all of these RX multicast workaround
> cases, things would be much easier.
> 
> The POLL and normal interrupt paths are already completely atomic for
> you already.  And since POLL cannot happen until open() completes
> that path would be safe too.
> 

Thanks for looking this over David.

It looks like I can do away with the need for this if I leave RXSTOP_INT
permanently enabled and use a flag in pdata to indicate whether the ISR
should do a multicast filter update.  Does this sound reasonable?

Please drop this patch.

Steve
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
index f513bdf..3f52d23 100644
--- a/drivers/net/smsc911x.c
+++ b/drivers/net/smsc911x.c
@@ -88,6 +88,9 @@  struct smsc911x_data {
 	 * unused with a 32-bit bus */
 	spinlock_t dev_lock;
 
+	/* spinlock to protect INT_EN read-modify-writes */
+	spinlock_t int_lock;
+
 	struct phy_device *phy_dev;
 	struct mii_bus *mii_bus;
 	int phy_irq[PHY_MAX_ADDR];
@@ -981,13 +984,16 @@  static int smsc911x_poll(struct napi_struct *napi, int budget)
 
 		if (!rxstat) {
 			unsigned int temp;
+			unsigned long flags;
 			/* We processed all packets available.  Tell NAPI it can
 			 * stop polling then re-enable rx interrupts */
 			smsc911x_reg_write(pdata, INT_STS, INT_STS_RSFL_);
 			netif_rx_complete(napi);
+			spin_lock_irqsave(&pdata->int_lock, flags);
 			temp = smsc911x_reg_read(pdata, INT_EN);
 			temp |= INT_EN_RSFL_EN_;
 			smsc911x_reg_write(pdata, INT_EN, temp);
+			spin_unlock_irqrestore(&pdata->int_lock, flags);
 			break;
 		}
 
@@ -1178,7 +1184,9 @@  static int smsc911x_open(struct net_device *dev)
 	spin_unlock_irq(&pdata->mac_lock);
 
 	/* Initialise irqs, but leave all sources disabled */
+	spin_lock_irq(&pdata->int_lock);
 	smsc911x_reg_write(pdata, INT_EN, 0);
+	spin_unlock_irq(&pdata->int_lock);
 	smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF);
 
 	/* Set interrupt deassertion to 100uS */
@@ -1204,9 +1212,11 @@  static int smsc911x_open(struct net_device *dev)
 	pdata->software_irq_signal = 0;
 	smp_wmb();
 
+	spin_lock_irq(&pdata->int_lock);
 	temp = smsc911x_reg_read(pdata, INT_EN);
 	temp |= INT_EN_SW_INT_EN_;
 	smsc911x_reg_write(pdata, INT_EN, temp);
+	spin_unlock_irq(&pdata->int_lock);
 
 	timeout = 1000;
 	while (timeout--) {
@@ -1245,9 +1255,11 @@  static int smsc911x_open(struct net_device *dev)
 	/* enable NAPI polling before enabling RX interrupts */
 	napi_enable(&pdata->napi);
 
+	spin_lock_irq(&pdata->int_lock);
 	temp = smsc911x_reg_read(pdata, INT_EN);
 	temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_);
 	smsc911x_reg_write(pdata, INT_EN, temp);
+	spin_unlock_irq(&pdata->int_lock);
 
 	spin_lock_irq(&pdata->mac_lock);
 	temp = smsc911x_mac_read(pdata, MAC_CR);
@@ -1419,9 +1431,11 @@  static void smsc911x_set_multicast_list(struct net_device *dev)
 			/* Request the hardware to stop, then perform the
 			 * update when we get an RX_STOP interrupt */
 			smsc911x_reg_write(pdata, INT_STS, INT_STS_RXSTOP_INT_);
+			spin_lock(&pdata->int_lock);
 			temp = smsc911x_reg_read(pdata, INT_EN);
 			temp |= INT_EN_RXSTOP_INT_EN_;
 			smsc911x_reg_write(pdata, INT_EN, temp);
+			spin_unlock(&pdata->int_lock);
 
 			temp = smsc911x_mac_read(pdata, MAC_CR);
 			temp &= ~(MAC_CR_RXEN_);
@@ -1445,12 +1459,15 @@  static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
 	u32 intsts = smsc911x_reg_read(pdata, INT_STS);
 	u32 inten = smsc911x_reg_read(pdata, INT_EN);
 	int serviced = IRQ_NONE;
+	unsigned long flags;
 	u32 temp;
 
 	if (unlikely(intsts & inten & INT_STS_SW_INT_)) {
+		spin_lock_irqsave(&pdata->int_lock, flags);
 		temp = smsc911x_reg_read(pdata, INT_EN);
 		temp &= (~INT_EN_SW_INT_EN_);
 		smsc911x_reg_write(pdata, INT_EN, temp);
+		spin_unlock_irqrestore(&pdata->int_lock, flags);
 		smsc911x_reg_write(pdata, INT_STS, INT_STS_SW_INT_);
 		pdata->software_irq_signal = 1;
 		smp_wmb();
@@ -1461,9 +1478,11 @@  static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
 		/* Called when there is a multicast update scheduled and
 		 * it is now safe to complete the update */
 		SMSC_TRACE(INTR, "RX Stop interrupt");
+		spin_lock_irqsave(&pdata->int_lock, flags);
 		temp = smsc911x_reg_read(pdata, INT_EN);
 		temp &= (~INT_EN_RXSTOP_INT_EN_);
 		smsc911x_reg_write(pdata, INT_EN, temp);
+		spin_unlock_irqrestore(&pdata->int_lock, flags);
 		smsc911x_reg_write(pdata, INT_STS, INT_STS_RXSTOP_INT_);
 		smsc911x_rx_multicast_update_workaround(pdata);
 		serviced = IRQ_HANDLED;
@@ -1487,9 +1506,11 @@  static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
 	if (likely(intsts & inten & INT_STS_RSFL_)) {
 		if (likely(netif_rx_schedule_prep(&pdata->napi))) {
 			/* Disable Rx interrupts */
+			spin_lock_irqsave(&pdata->int_lock, flags);
 			temp = smsc911x_reg_read(pdata, INT_EN);
 			temp &= (~INT_EN_RSFL_EN_);
 			smsc911x_reg_write(pdata, INT_EN, temp);
+			spin_unlock_irqrestore(&pdata->int_lock, flags);
 			/* Schedule a NAPI poll */
 			__netif_rx_schedule(&pdata->napi);
 		} else {
@@ -1760,6 +1781,7 @@  static int __devinit smsc911x_init(struct net_device *dev)
 	SMSC_TRACE(PROBE, "PHY will be autodetected.");
 
 	spin_lock_init(&pdata->dev_lock);
+	spin_lock_init(&pdata->int_lock);
 
 	if (pdata->ioaddr == 0) {
 		SMSC_WARNING(PROBE, "pdata->ioaddr: 0x00000000");