[PATCH v3] tty: serial: max3100: shut down timer before freeing port
Fan Wu <[email protected]> Wed, 5 Aug 2026 00:40:39 +0000
| Newsgroups | org.kernel.vger.linux-serial,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
max3100_shutdown() stops the polling timer but returns early during
system suspend. If the SPI device is unbound before resume, the serial
core does not call max3100_shutdown() again, so max3100_remove() frees
the port while the timer remains armed. max3100_timeout() may then
access the freed port and re-arm the timer.
Add final timer teardown to max3100_remove() and use
timer_shutdown_sync() to prevent a racing callback from re-arming it.
Also free the IRQ and destroy the workqueue there before freeing the
port. Keep timer_delete_sync() in max3100_shutdown() so that a
subsequent open() can re-arm the timer.
The workqueue is created before request_irq() and destroyed on both
request_irq() failure and normal shutdown. Its presence at remove thus
identifies the IRQ left registered when suspend bypasses shutdown.
This issue was found by an in-house static analysis tool.
Fixes: 7831d56b0a35 ("tty: MAX3100")
Cc: [email protected] # 6.2+
Assisted-by: Codex:gpt-5.6
Signed-off-by: Fan Wu <[email protected]>
---
Changes since v2:
- Drop irq_registered; use the workqueue lifetime to decide whether
remove must release an IRQ left by the suspend path.
v1: https://lore.kernel.org/all/[email protected]/
v2: https://lore.kernel.org/all/[email protected]/
---
drivers/tty/serial/max3100.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index 44b745fa26c6..48c66b6e1c18 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -537,9 +537,8 @@ static void max3100_shutdown(struct uart_port *port)
if (s->workqueue) {
destroy_workqueue(s->workqueue);
s->workqueue = NULL;
- }
- if (port->irq)
free_irq(port->irq, s);
+ }
/* set shutdown mode to save power */
max3100_sr(s, MAX3100_WC | MAX3100_SHDN, &rx);
@@ -752,6 +751,14 @@ static void max3100_remove(struct spi_device *spi)
if (max3100s[i] == s) {
dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i);
uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port);
+
+ s->force_end_work = 1;
+ timer_shutdown_sync(&s->timer);
+ if (s->workqueue) {
+ destroy_workqueue(s->workqueue);
+ s->workqueue = NULL;
+ free_irq(s->port.irq, s);
+ }
kfree(max3100s[i]);
max3100s[i] = NULL;
break;
--
2.34.1