Commit 85ba8f52 authored by Solomon Peachy's avatar Solomon Peachy Committed by John W. Linville

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: default avatarSolomon Peachy <pizza@shaftnet.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent aec8e88c
...@@ -40,6 +40,7 @@ struct hwbus_priv { ...@@ -40,6 +40,7 @@ struct hwbus_priv {
struct cw1200_common *core; struct cw1200_common *core;
const struct cw1200_platform_data_spi *pdata; const struct cw1200_platform_data_spi *pdata;
spinlock_t lock; /* Serialize all bus operations */ spinlock_t lock; /* Serialize all bus operations */
wait_queue_head_t wq;
int claimed; int claimed;
int irq_disabled; int irq_disabled;
}; };
...@@ -198,8 +199,11 @@ static void cw1200_spi_lock(struct hwbus_priv *self) ...@@ -198,8 +199,11 @@ static void cw1200_spi_lock(struct hwbus_priv *self)
{ {
unsigned long flags; unsigned long flags;
DECLARE_WAITQUEUE(wait, current);
might_sleep(); might_sleep();
add_wait_queue(&self->wq, &wait);
spin_lock_irqsave(&self->lock, flags); spin_lock_irqsave(&self->lock, flags);
while (1) { while (1) {
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
...@@ -212,6 +216,7 @@ static void cw1200_spi_lock(struct hwbus_priv *self) ...@@ -212,6 +216,7 @@ static void cw1200_spi_lock(struct hwbus_priv *self)
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
self->claimed = 1; self->claimed = 1;
spin_unlock_irqrestore(&self->lock, flags); spin_unlock_irqrestore(&self->lock, flags);
remove_wait_queue(&self->wq, &wait);
return; return;
} }
...@@ -223,6 +228,8 @@ static void cw1200_spi_unlock(struct hwbus_priv *self) ...@@ -223,6 +228,8 @@ static void cw1200_spi_unlock(struct hwbus_priv *self)
spin_lock_irqsave(&self->lock, flags); spin_lock_irqsave(&self->lock, flags);
self->claimed = 0; self->claimed = 0;
spin_unlock_irqrestore(&self->lock, flags); spin_unlock_irqrestore(&self->lock, flags);
wake_up(&self->wq);
return; return;
} }
...@@ -413,6 +420,8 @@ static int cw1200_spi_probe(struct spi_device *func) ...@@ -413,6 +420,8 @@ static int cw1200_spi_probe(struct spi_device *func)
spi_set_drvdata(func, self); spi_set_drvdata(func, self);
init_waitqueue_head(&self->wq);
status = cw1200_spi_irq_subscribe(self); status = cw1200_spi_irq_subscribe(self);
status = cw1200_core_probe(&cw1200_spi_hwbus_ops, status = cw1200_core_probe(&cw1200_spi_hwbus_ops,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment