Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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,