2013-08-28 00:29:52

by Solomon Peachy

[permalink] [raw]
Subject: [PATCH 1/2] cw1200: Don't perform SPI transfers in interrupt context

When we get an interrupt from the hardware, the first thing the driver does
is tell the device to mask off the interrupt line. Unfortunately this
involves a SPI transaction in interrupt context. Some (most?) SPI
controllers perform the transfer asynchronously and try to sleep.
This is bad, and triggers a BUG().

So, work around this by using adding a hwbus hook for the cw1200 driver
core to call. The cw1200_spi driver translates this into
irq_disable()/irq_enable() calls instead, which can safely be called in
interrupt context.

Apparently the platforms I used to develop the cw1200_spi driver used
synchronous spi_sync() implementations, which is why this didn't surface
until now.

Many thanks to Dave Sizeburns for the inital bug report and his services
as a tester.

Signed-off-by: Solomon Peachy <[email protected]>
---
Please consider this for 3.11-rc; without this patch many SPI users
will immediately trigger a BUG().

drivers/net/wireless/cw1200/cw1200_spi.c | 19 ++++++++++++++++---
drivers/net/wireless/cw1200/fwio.c | 2 +-
drivers/net/wireless/cw1200/hwbus.h | 1 +
drivers/net/wireless/cw1200/hwio.c | 15 +++++++++++++++
4 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c
index d063760..c31580b 100644
--- a/drivers/net/wireless/cw1200/cw1200_spi.c
+++ b/drivers/net/wireless/cw1200/cw1200_spi.c
@@ -41,6 +41,7 @@ struct hwbus_priv {
const struct cw1200_platform_data_spi *pdata;
spinlock_t lock; /* Serialize all bus operations */
int claimed;
+ int irq_disabled;
};

#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
@@ -230,6 +231,8 @@ static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
struct hwbus_priv *self = dev_id;

if (self->core) {
+ disable_irq_nosync(self->func->irq);
+ self->irq_disabled = 1;
cw1200_irq_handler(self->core);
return IRQ_HANDLED;
} else {
@@ -263,13 +266,22 @@ exit:

static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self)
{
- int ret = 0;
-
pr_debug("SW IRQ unsubscribe\n");
disable_irq_wake(self->func->irq);
free_irq(self->func->irq, self);

- return ret;
+ return 0;
+}
+
+static int cw1200_spi_irq_enable(struct hwbus_priv *self, int enable)
+{
+ /* Disables are handled by the interrupt handler */
+ if (enable && self->irq_disabled) {
+ enable_irq(self->func->irq);
+ self->irq_disabled = 0;
+ }
+
+ return 0;
}

static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
@@ -349,6 +361,7 @@ static struct hwbus_ops cw1200_spi_hwbus_ops = {
.unlock = cw1200_spi_unlock,
.align_size = cw1200_spi_align_size,
.power_mgmt = cw1200_spi_pm,
+ .irq_enable = cw1200_spi_irq_enable,
};

/* Probe Function to be called by SPI stack when device is discovered */
diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c
index acdff0f..0b2061b 100644
--- a/drivers/net/wireless/cw1200/fwio.c
+++ b/drivers/net/wireless/cw1200/fwio.c
@@ -485,7 +485,7 @@ int cw1200_load_firmware(struct cw1200_common *priv)

/* Enable interrupt signalling */
priv->hwbus_ops->lock(priv->hwbus_priv);
- ret = __cw1200_irq_enable(priv, 1);
+ ret = __cw1200_irq_enable(priv, 2);
priv->hwbus_ops->unlock(priv->hwbus_priv);
if (ret < 0)
goto unsubscribe;
diff --git a/drivers/net/wireless/cw1200/hwbus.h b/drivers/net/wireless/cw1200/hwbus.h
index 8b2fc83..51dfb3a 100644
--- a/drivers/net/wireless/cw1200/hwbus.h
+++ b/drivers/net/wireless/cw1200/hwbus.h
@@ -28,6 +28,7 @@ struct hwbus_ops {
void (*unlock)(struct hwbus_priv *self);
size_t (*align_size)(struct hwbus_priv *self, size_t size);
int (*power_mgmt)(struct hwbus_priv *self, bool suspend);
+ int (*irq_enable)(struct hwbus_priv *self, int enable);
};

#endif /* CW1200_HWBUS_H */
diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/cw1200/hwio.c
index ff230b7..41bd761 100644
--- a/drivers/net/wireless/cw1200/hwio.c
+++ b/drivers/net/wireless/cw1200/hwio.c
@@ -273,6 +273,21 @@ int __cw1200_irq_enable(struct cw1200_common *priv, int enable)
u16 val16;
int ret;

+ /* We need to do this hack because the SPI layer can sleep on I/O
+ and the general path involves I/O to the device in interrupt
+ context.
+
+ However, the initial enable call needs to go to the hardware.
+
+ We don't worry about shutdown because we do a full reset which
+ clears the interrupt enabled bits.
+ */
+ if (priv->hwbus_ops->irq_enable) {
+ ret = priv->hwbus_ops->irq_enable(priv->hwbus_priv, enable);
+ if (ret || enable < 2)
+ return ret;
+ }
+
if (HIF_8601_SILICON == priv->hw_type) {
ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
--
1.8.3.1



2013-08-28 00:29:53

by Solomon Peachy

[permalink] [raw]
Subject: [PATCH 2/2] cw1200: Prevent a lock-related hang in the cw1200_spi driver

The cw1200_spi driver tries to mirror the cw1200_sdio driver's lock
API, which relies on sdio_claim_host/sdio_release_host to serialize
hardware operations across multiple threads.

Unfortunately the implementation was flawed, as it lacked a way to wake
up the lock requestor when there was contention, often resulting in a
hang.

This problem was uncovered while trying to fix the
spi-transfers-in-interrupt-context BUG() corrected in the previous
patch. Many thanks to Dave Sizeburns for his assistance in fixing this.

Signed-off-by: Solomon Peachy <[email protected]>
---
Please consider this for 3.11-rc; without this patch SPI users are all
but guaranteed to deadlock while running this driver.

drivers/net/wireless/cw1200/cw1200_spi.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c
index c31580b..f5e6b48 100644
--- a/drivers/net/wireless/cw1200/cw1200_spi.c
+++ b/drivers/net/wireless/cw1200/cw1200_spi.c
@@ -40,6 +40,7 @@ struct hwbus_priv {
struct cw1200_common *core;
const struct cw1200_platform_data_spi *pdata;
spinlock_t lock; /* Serialize all bus operations */
+ wait_queue_head_t wq;
int claimed;
int irq_disabled;
};
@@ -198,8 +199,11 @@ static void cw1200_spi_lock(struct hwbus_priv *self)
{
unsigned long flags;

+ DECLARE_WAITQUEUE(wait, current);
+
might_sleep();

+ add_wait_queue(&self->wq, &wait);
spin_lock_irqsave(&self->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -212,6 +216,7 @@ static void cw1200_spi_lock(struct hwbus_priv *self)
set_current_state(TASK_RUNNING);
self->claimed = 1;
spin_unlock_irqrestore(&self->lock, flags);
+ remove_wait_queue(&self->wq, &wait);

return;
}
@@ -223,6 +228,8 @@ static void cw1200_spi_unlock(struct hwbus_priv *self)
spin_lock_irqsave(&self->lock, flags);
self->claimed = 0;
spin_unlock_irqrestore(&self->lock, flags);
+ wake_up(&self->wq);
+
return;
}

@@ -413,6 +420,8 @@ static int cw1200_spi_probe(struct spi_device *func)

spi_set_drvdata(func, self);

+ init_waitqueue_head(&self->wq);
+
status = cw1200_spi_irq_subscribe(self);

status = cw1200_core_probe(&cw1200_spi_hwbus_ops,
--
1.8.3.1


2013-09-03 11:58:58

by Solomon Peachy

[permalink] [raw]
Subject: Re: [PATCH 1/2] cw1200: Don't perform SPI transfers in interrupt context

On Tue, Aug 27, 2013 at 08:29:46PM -0400, Solomon Peachy wrote:
> When we get an interrupt from the hardware, the first thing the driver does
> is tell the device to mask off the interrupt line. Unfortunately this
> involves a SPI transaction in interrupt context. Some (most?) SPI
> controllers perform the transfer asynchronously and try to sleep.
> This is bad, and triggers a BUG().

Did this patch series get dropped? I saw that the followup series
of minor cleanups were merged into wireless-next, but not these.

Since they didn't make it into 3.11, I intend to submit them to -stable,
but I need to make sure they at least make it into -next.

Thanks,

- Solomon
--
Solomon Peachy pizza at shaftnet dot org
Delray Beach, FL ^^ (email/xmpp) ^^
Quidquid latine dictum sit, altum viditur.


Attachments:
(No filename) (842.00 B)
(No filename) (190.00 B)
Download all attachments

2013-09-09 18:45:12

by John W. Linville

[permalink] [raw]
Subject: Re: [PATCH 1/2] cw1200: Don't perform SPI transfers in interrupt context

On Tue, Sep 03, 2013 at 07:58:57AM -0400, Solomon Peachy wrote:
> On Tue, Aug 27, 2013 at 08:29:46PM -0400, Solomon Peachy wrote:
> > When we get an interrupt from the hardware, the first thing the driver does
> > is tell the device to mask off the interrupt line. Unfortunately this
> > involves a SPI transaction in interrupt context. Some (most?) SPI
> > controllers perform the transfer asynchronously and try to sleep.
> > This is bad, and triggers a BUG().
>
> Did this patch series get dropped? I saw that the followup series
> of minor cleanups were merged into wireless-next, but not these.
>
> Since they didn't make it into 3.11, I intend to submit them to -stable,
> but I need to make sure they at least make it into -next.

I'll be sending them for 3.12 soon...

--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.

2013-09-14 02:47:19

by Solomon Peachy

[permalink] [raw]
Subject: Re: [PATCH 1/2] cw1200: Don't perform SPI transfers in interrupt context

On Mon, Sep 09, 2013 at 02:33:50PM -0400, John W. Linville wrote:
> > Since they didn't make it into 3.11, I intend to submit them to -stable,
> > but I need to make sure they at least make it into -next.
>
> I'll be sending them for 3.12 soon...

Can you please revert this commit? (aec8e88c947b7017e2b4bbcb68a4bfc4a1f8ad35)

It ends up creating horrible interrupt losses.

I'll have a much simpler replacement posted shortly.

- Solomon
--
Solomon Peachy pizza at shaftnet dot org
Delray Beach, FL ^^ (email/xmpp) ^^
Quidquid latine dictum sit, altum viditur.


Attachments:
(No filename) (608.00 B)
(No filename) (190.00 B)
Download all attachments