Skip to content

Commit 5f1f7ce

Browse files
Fix max3421-hcd race that permanently disables the USB interrupt (#31)
Patch max3421 kernel thread to IRQ pair operation atomic
1 parent 785ea54 commit 5f1f7ce

1 file changed

Lines changed: 147 additions & 0 deletions

File tree

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
From d620b6d96be120677cb4072dc253368ca8e96ebd Mon Sep 17 00:00:00 2001
2+
From: Rob Kleffner <rob@oscium.com>
3+
Date: Wed, 22 Jul 2026 19:35:39 -0600
4+
Subject: [PATCH] usb: host: max3421-hcd: fix SMP race that leaves the IRQ
5+
disabled forever
6+
7+
The hard interrupt handler and the SPI thread hand ownership of the
8+
interrupt line back and forth through the ENABLE_IRQ bit in
9+
max3421_hcd->todo. The handler does
10+
11+
if (!test_and_set_bit(ENABLE_IRQ, &max3421_hcd->todo))
12+
disable_irq_nosync(spi->irq);
13+
14+
and the SPI thread's idle path does
15+
16+
if (test_and_clear_bit(ENABLE_IRQ, &max3421_hcd->todo))
17+
enable_irq(spi->irq);
18+
19+
Each bit operation is atomic, but nothing makes the {bit operation,
20+
IRQ-depth operation} pair atomic on either side. On an SMP system the
21+
thread's test_and_clear_bit() can land between the handler's
22+
test_and_set_bit() and its disable_irq_nosync(). The thread then calls
23+
enable_irq() while the line is still enabled:
24+
25+
WARNING: CPU: 1 PID: 245 at kernel/irq/manage.c:790 __enable_irq+0x54/0x90
26+
Unbalanced enable for IRQ 37
27+
Call trace:
28+
__enable_irq+0x54/0x90
29+
enable_irq+0x58/0xb8
30+
max3421_spi_thread+0x850/0xbd0
31+
32+
and the handler's disable_irq_nosync() lands afterwards, leaving the
33+
line masked at depth 1 with ENABLE_IRQ clear. From then on the thread's
34+
idle path never sees the bit set, so nothing ever re-enables the IRQ:
35+
the chip's interrupt is dead, in-flight transfers never complete, and
36+
the attached device eventually drops off the bus after a storm of
37+
-ETIMEDOUT resets. Only unbinding and rebinding the driver (or a
38+
reboot) recovers.
39+
40+
Observed in the field on a quad-core Raspberry Pi CM4 driving a
41+
full-speed spectrum analyzer: the chip's ~1 kHz FRAME interrupt cadence
42+
exercises the race window about a thousand times per second, and
43+
continuous bulk-IN streaming kills the bus within minutes to hours.
44+
45+
Serialize both pairs with a dedicated spinlock. The handler runs in
46+
hard-IRQ context with local interrupts off, so a plain spin_lock() is
47+
sufficient there; the thread side takes the lock with
48+
spin_lock_irqsave() so the handler cannot preempt it on the same CPU
49+
inside the critical section and spin on the lock forever. The lock is
50+
initialized in probe before the SPI thread starts, because the thread's
51+
idle path takes it from its very first iteration.
52+
53+
Also move the handler's wake_up_process() after the locked section. A
54+
wakeup issued before ENABLE_IRQ is published can be spent on the thread
55+
passing its idle check while the bit is still clear and then going to
56+
sleep; the freshly-set bit is then not noticed until some unrelated
57+
wakeup (a URB submission or hub activity) happens along, stalling the
58+
stream in the meantime.
59+
60+
Fixes: 2d53139f3162 ("Add support for using a MAX3421E chip as a host driver.")
61+
Cc: stable@vger.kernel.org
62+
---
63+
drivers/usb/host/max3421-hcd.c | 39 ++++++++++++++++++++++++++++++++--
64+
1 file changed, 37 insertions(+), 2 deletions(-)
65+
66+
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
67+
index 52b0fcc..3d3333d 100644
68+
--- a/drivers/usb/host/max3421-hcd.c
69+
+++ b/drivers/usb/host/max3421-hcd.c
70+
@@ -123,6 +123,17 @@ struct max3421_dma_buf {
71+
struct max3421_hcd {
72+
spinlock_t lock;
73+
74+
+ /*
75+
+ * Serializes the {ENABLE_IRQ bit, interrupt-line disable/enable}
76+
+ * pairs in max3421_irq_handler() and the SPI thread's idle path.
77+
+ * With the atomic bit alone the thread's test_and_clear_bit() can
78+
+ * slip between the handler's test_and_set_bit() and its
79+
+ * disable_irq_nosync(); the thread then enables an already-enabled
80+
+ * line ("Unbalanced enable for IRQ") and the handler's disable is
81+
+ * never paired again, leaving the interrupt masked for good.
82+
+ */
83+
+ spinlock_t irq_lock;
84+
+
85+
struct task_struct *spi_thread;
86+
87+
enum max3421_rh_state rh_state;
88+
@@ -1145,10 +1156,19 @@ max3421_irq_handler(int irq, void *dev_id)
89+
struct spi_device *spi = to_spi_device(hcd->self.controller);
90+
struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
91+
92+
- if (max3421_hcd->spi_thread)
93+
- wake_up_process(max3421_hcd->spi_thread);
94+
+ /* In hard-IRQ context local interrupts are off: plain spin_lock. */
95+
+ spin_lock(&max3421_hcd->irq_lock);
96+
if (!test_and_set_bit(ENABLE_IRQ, &max3421_hcd->todo))
97+
disable_irq_nosync(spi->irq);
98+
+ spin_unlock(&max3421_hcd->irq_lock);
99+
+ /*
100+
+ * Wake the thread only after ENABLE_IRQ is published: a wakeup
101+
+ * issued before the bit is set can be spent on the thread passing
102+
+ * its idle check (bit still clear) and going to sleep, and this
103+
+ * interrupt then sits unnoticed until an unrelated wakeup arrives.
104+
+ */
105+
+ if (max3421_hcd->spi_thread)
106+
+ wake_up_process(max3421_hcd->spi_thread);
107+
return IRQ_HANDLED;
108+
}
109+
110+
@@ -1383,6 +1403,7 @@ max3421_spi_thread(void *dev_id)
111+
struct spi_device *spi = to_spi_device(hcd->self.controller);
112+
struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
113+
int i, i_worked = 1;
114+
+ unsigned long flags;
115+
116+
/* set full-duplex SPI mode, low-active interrupt pin: */
117+
spi_wr8(hcd, MAX3421_REG_PINCTL,
118+
@@ -1410,8 +1431,15 @@ max3421_spi_thread(void *dev_id)
119+
spi_wr8(hcd, MAX3421_REG_HIEN, max3421_hcd->hien);
120+
121+
set_current_state(TASK_INTERRUPTIBLE);
122+
+ /*
123+
+ * _irqsave: if the MAX3421 interrupt fired on this
124+
+ * CPU inside the critical section, its handler
125+
+ * would spin on irq_lock forever.
126+
+ */
127+
+ spin_lock_irqsave(&max3421_hcd->irq_lock, flags);
128+
if (test_and_clear_bit(ENABLE_IRQ, &max3421_hcd->todo))
129+
enable_irq(spi->irq);
130+
+ spin_unlock_irqrestore(&max3421_hcd->irq_lock, flags);
131+
schedule();
132+
__set_current_state(TASK_RUNNING);
133+
}
134+
@@ -1887,6 +1915,13 @@ max3421_probe(struct spi_device *spi)
135+
INIT_LIST_HEAD(&max3421_hcd->ep_list);
136+
spi_set_drvdata(spi, max3421_hcd);
137+
138+
+ /*
139+
+ * Initialize before kthread_run() and request_irq(): the SPI
140+
+ * thread's idle path takes irq_lock from its first iteration
141+
+ * (max3421_start() would be too late).
142+
+ */
143+
+ spin_lock_init(&max3421_hcd->irq_lock);
144+
+
145+
max3421_hcd->tx = kmalloc(sizeof(*max3421_hcd->tx), GFP_KERNEL);
146+
if (!max3421_hcd->tx)
147+
goto error;

0 commit comments

Comments
 (0)