[PATCH] ipw2200 locking fix
Well, this is not 100% if when the card fires two consecutive interrupts. Though unlikely, it's better to protect early than seeing some "weird" bugs one day. I proposed attached patch. If you can help to test, that will be appreciated (I cannot see the lockdep warning on my box somehow). Cc: Frederik Deweerdt <deweerdt@free.fr> Cc: Arjan van de Ven <arjan@infradead.org> Cc: Ingo Molnar <mingo@elte.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
@@ -533,7 +533,7 @@ static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
|
|||||||
ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
|
ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ipw_enable_interrupts(struct ipw_priv *priv)
|
static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
|
||||||
{
|
{
|
||||||
if (priv->status & STATUS_INT_ENABLED)
|
if (priv->status & STATUS_INT_ENABLED)
|
||||||
return;
|
return;
|
||||||
@@ -541,7 +541,7 @@ static inline void ipw_enable_interrupts(struct ipw_priv *priv)
|
|||||||
ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
|
ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ipw_disable_interrupts(struct ipw_priv *priv)
|
static inline void __ipw_disable_interrupts(struct ipw_priv *priv)
|
||||||
{
|
{
|
||||||
if (!(priv->status & STATUS_INT_ENABLED))
|
if (!(priv->status & STATUS_INT_ENABLED))
|
||||||
return;
|
return;
|
||||||
@@ -549,6 +549,24 @@ static inline void ipw_disable_interrupts(struct ipw_priv *priv)
|
|||||||
ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
|
ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void ipw_enable_interrupts(struct ipw_priv *priv)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&priv->irq_lock, flags);
|
||||||
|
__ipw_enable_interrupts(priv);
|
||||||
|
spin_unlock_irqrestore(&priv->irq_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ipw_disable_interrupts(struct ipw_priv *priv)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&priv->irq_lock, flags);
|
||||||
|
__ipw_disable_interrupts(priv);
|
||||||
|
spin_unlock_irqrestore(&priv->irq_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_IPW2200_DEBUG
|
#ifdef CONFIG_IPW2200_DEBUG
|
||||||
static char *ipw_error_desc(u32 val)
|
static char *ipw_error_desc(u32 val)
|
||||||
{
|
{
|
||||||
@@ -1856,7 +1874,7 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&priv->lock, flags);
|
spin_lock_irqsave(&priv->irq_lock, flags);
|
||||||
|
|
||||||
inta = ipw_read32(priv, IPW_INTA_RW);
|
inta = ipw_read32(priv, IPW_INTA_RW);
|
||||||
inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
|
inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
|
||||||
@@ -1865,6 +1883,10 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
|
|||||||
/* Add any cached INTA values that need to be handled */
|
/* Add any cached INTA values that need to be handled */
|
||||||
inta |= priv->isr_inta;
|
inta |= priv->isr_inta;
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&priv->irq_lock, flags);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&priv->lock, flags);
|
||||||
|
|
||||||
/* handle all the justifications for the interrupt */
|
/* handle all the justifications for the interrupt */
|
||||||
if (inta & IPW_INTA_BIT_RX_TRANSFER) {
|
if (inta & IPW_INTA_BIT_RX_TRANSFER) {
|
||||||
ipw_rx(priv);
|
ipw_rx(priv);
|
||||||
@@ -1993,10 +2015,10 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
|
|||||||
IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
|
IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
|
|
||||||
/* enable all interrupts */
|
/* enable all interrupts */
|
||||||
ipw_enable_interrupts(priv);
|
ipw_enable_interrupts(priv);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&priv->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
|
#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
|
||||||
@@ -10460,7 +10482,7 @@ static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
|
|||||||
if (!priv)
|
if (!priv)
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
spin_lock(&priv->lock);
|
spin_lock(&priv->irq_lock);
|
||||||
|
|
||||||
if (!(priv->status & STATUS_INT_ENABLED)) {
|
if (!(priv->status & STATUS_INT_ENABLED)) {
|
||||||
/* Shared IRQ */
|
/* Shared IRQ */
|
||||||
@@ -10482,7 +10504,7 @@ static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* tell the device to stop sending interrupts */
|
/* tell the device to stop sending interrupts */
|
||||||
ipw_disable_interrupts(priv);
|
__ipw_disable_interrupts(priv);
|
||||||
|
|
||||||
/* ack current interrupts */
|
/* ack current interrupts */
|
||||||
inta &= (IPW_INTA_MASK_ALL & inta_mask);
|
inta &= (IPW_INTA_MASK_ALL & inta_mask);
|
||||||
@@ -10493,11 +10515,11 @@ static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
|
|||||||
|
|
||||||
tasklet_schedule(&priv->irq_tasklet);
|
tasklet_schedule(&priv->irq_tasklet);
|
||||||
|
|
||||||
spin_unlock(&priv->lock);
|
spin_unlock(&priv->irq_lock);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
none:
|
none:
|
||||||
spin_unlock(&priv->lock);
|
spin_unlock(&priv->irq_lock);
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11477,6 +11499,7 @@ static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
#ifdef CONFIG_IPW2200_DEBUG
|
#ifdef CONFIG_IPW2200_DEBUG
|
||||||
ipw_debug_level = debug;
|
ipw_debug_level = debug;
|
||||||
#endif
|
#endif
|
||||||
|
spin_lock_init(&priv->irq_lock);
|
||||||
spin_lock_init(&priv->lock);
|
spin_lock_init(&priv->lock);
|
||||||
for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++)
|
for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++)
|
||||||
INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
|
INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
|
||||||
|
@@ -1173,6 +1173,7 @@ struct ipw_priv {
|
|||||||
struct ieee80211_device *ieee;
|
struct ieee80211_device *ieee;
|
||||||
|
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
spinlock_t irq_lock;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
|
|
||||||
/* basic pci-network driver stuff */
|
/* basic pci-network driver stuff */
|
||||||
|
Reference in New Issue
Block a user