[PATCH v3 1/5] Input: applespi - use unified wait queue with timeouts for drain
From: Shih-Yuan Lee
Date: Mon Jul 20 2026 - 12:57:07 EST
Consolidate wait queues by renaming drain_complete to wait_queue and use
wait_event_lock_irq_timeout() with a 3-second timeout during read and write
drains to prevent indefinite lockups during driver unbind or PM transitions
if the hardware becomes unresponsive.
If a timeout occurs while a read or write transfer is still active, issue
a warning and fallback to waiting unconditionally until the transfer completion
callback clears the active flag. This guarantees that applespi_drain_writes()
and applespi_drain_reads() never return prematurely while transfers are in flight,
preventing use-after-free (UAF) and DMA memory corruption when devres frees
the driver context and DMA buffers during unbind.
Signed-off-by: Shih-Yuan Lee <fourdollars@xxxxxxxxxx>
---
drivers/input/keyboard/applespi.c | 48 ++++++++++++++++++++++++-------
1 file changed, 38 insertions(+), 10 deletions(-)
diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
index b5ff71cd5a70..c501bf7e517e 100644
--- a/drivers/input/keyboard/applespi.c
+++ b/drivers/input/keyboard/applespi.c
@@ -417,7 +417,7 @@ struct applespi_data {
bool suspended;
bool drain;
- wait_queue_head_t drain_complete;
+ wait_queue_head_t wait_queue;
bool read_active;
bool write_active;
@@ -677,7 +677,7 @@ static int applespi_setup_spi(struct applespi_data *applespi)
return sts;
spin_lock_init(&applespi->cmd_msg_lock);
- init_waitqueue_head(&applespi->drain_complete);
+ init_waitqueue_head(&applespi->wait_queue);
return 0;
}
@@ -725,7 +725,7 @@ static void applespi_msg_complete(struct applespi_data *applespi,
applespi->write_active = false;
if (applespi->drain && !applespi->write_active)
- wake_up_all(&applespi->drain_complete);
+ wake_up_all(&applespi->wait_queue);
if (is_write_msg) {
applespi->cmd_msg_queued = 0;
@@ -1415,7 +1415,7 @@ static void applespi_got_data(struct applespi_data *applespi)
applespi->read_active = false;
applespi->write_active = false;
- wake_up_all(&applespi->drain_complete);
+ wake_up_all(&applespi->wait_queue);
}
return;
@@ -1793,21 +1793,49 @@ static int applespi_probe(struct spi_device *spi)
static void applespi_drain_writes(struct applespi_data *applespi)
{
- guard(spinlock_irqsave)(&applespi->cmd_msg_lock);
+ unsigned long flags;
+ long ret;
+
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
applespi->drain = true;
- wait_event_lock_irq(applespi->drain_complete, !applespi->write_active,
- applespi->cmd_msg_lock);
+ ret = wait_event_lock_irq_timeout(applespi->wait_queue,
+ !applespi->write_active,
+ applespi->cmd_msg_lock,
+ msecs_to_jiffies(3000));
+ if (!ret && applespi->write_active) {
+ dev_warn(&applespi->spi->dev,
+ "Timed out waiting for write drain, waiting unconditionally\n");
+ wait_event_lock_irq(applespi->wait_queue,
+ !applespi->write_active,
+ applespi->cmd_msg_lock);
+ }
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
}
static void applespi_drain_reads(struct applespi_data *applespi)
{
- guard(spinlock_irqsave)(&applespi->cmd_msg_lock);
+ unsigned long flags;
+ long ret;
- wait_event_lock_irq(applespi->drain_complete, !applespi->read_active,
- applespi->cmd_msg_lock);
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+ ret = wait_event_lock_irq_timeout(applespi->wait_queue,
+ !applespi->read_active,
+ applespi->cmd_msg_lock,
+ msecs_to_jiffies(3000));
+ if (!ret && applespi->read_active) {
+ dev_warn(&applespi->spi->dev,
+ "Timed out waiting for read drain, waiting unconditionally\n");
+ wait_event_lock_irq(applespi->wait_queue,
+ !applespi->read_active,
+ applespi->cmd_msg_lock);
+ }
applespi->suspended = true;
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
}
static void applespi_remove(struct spi_device *spi)
--
2.39.5