mirror of https://gitee.com/openkylin/linux.git
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 <pizza@shaftnet.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
aec8e88c94
commit
85ba8f529c
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue