-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
When /dev/ttySTM1 is closed while there are still some characters pending to be sent, the kernel dereferences a null pointer which locks up the kernel. This is fixed by two patches cherry-picked from next. Another problem is that if the UART is closed while being throttled, the irq handler thinks it's still throttled after reopen which results in it not handling RX events resulting in a stuck irq starving the machine. This is fixed by two further patches. The first only improves the driver's behaviour generally on stuck irq, the second is the actual fix. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
- Loading branch information
Uwe Kleine-König
committed
Apr 22, 2024
1 parent
cb4386e
commit bc2c396
Showing
20 changed files
with
377 additions
and
29 deletions.
There are no files selected for viewing
54 changes: 54 additions & 0 deletions
54
...ernel/linux/files/patches/0001-serial-core-Clearing-the-circular-buffer-before-NULL.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> | ||
Date: Thu, 4 Apr 2024 17:59:26 +0300 | ||
Subject: [PATCH] serial: core: Clearing the circular buffer before NULLifying | ||
it | ||
|
||
The circular buffer is NULLified in uart_tty_port_shutdown() | ||
under the spin lock. However, the PM or other timer based callbacks | ||
may still trigger after this event without knowning that buffer pointer | ||
is not valid. Since the serial code is a bit inconsistent in checking | ||
the buffer state (some rely on the head-tail positions, some on the | ||
buffer pointer), it's better to have both aligned, i.e. buffer pointer | ||
to be NULL and head-tail possitions to be the same, meaning it's empty. | ||
This will prevent asynchronous calls to dereference NULL pointer as | ||
reported recently in 8250 case: | ||
|
||
BUG: kernel NULL pointer dereference, address: 00000cf5 | ||
Workqueue: pm pm_runtime_work | ||
EIP: serial8250_tx_chars (drivers/tty/serial/8250/8250_port.c:1809) | ||
... | ||
? serial8250_tx_chars (drivers/tty/serial/8250/8250_port.c:1809) | ||
__start_tx (drivers/tty/serial/8250/8250_port.c:1551) | ||
serial8250_start_tx (drivers/tty/serial/8250/8250_port.c:1654) | ||
serial_port_runtime_suspend (include/linux/serial_core.h:667 drivers/tty/serial/serial_port.c:63) | ||
__rpm_callback (drivers/base/power/runtime.c:393) | ||
? serial_port_remove (drivers/tty/serial/serial_port.c:50) | ||
rpm_suspend (drivers/base/power/runtime.c:447) | ||
|
||
The proposed change will prevent ->start_tx() to be called during | ||
suspend on shut down port. | ||
|
||
Fixes: 43066e32227e ("serial: port: Don't suspend if the port is still busy") | ||
Cc: stable <stable@kernel.org> | ||
Reported-by: kernel test robot <oliver.sang@intel.com> | ||
Closes: https://lore.kernel.org/oe-lkp/202404031607.2e92eebe-lkp@intel.com | ||
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> | ||
Link: https://lore.kernel.org/r/20240404150034.41648-1-andriy.shevchenko@linux.intel.com | ||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||
Origin: next-20240410, commit:9cf7ea2eeb745213dc2a04103e426b960e807940 | ||
--- | ||
drivers/tty/serial/serial_core.c | 1 + | ||
1 file changed, 1 insertion(+) | ||
|
||
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c | ||
index d6a58a9e072a..15664cda4fcd 100644 | ||
--- a/drivers/tty/serial/serial_core.c | ||
+++ b/drivers/tty/serial/serial_core.c | ||
@@ -1788,6 +1788,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) | ||
* Free the transmit buffer. | ||
*/ | ||
uart_port_lock_irq(uport); | ||
+ uart_circ_clear(&state->xmit); | ||
buf = state->xmit.buf; | ||
state->xmit.buf = NULL; | ||
uart_port_unlock_irq(uport); |
161 changes: 161 additions & 0 deletions
161
...ernel/linux/files/patches/0002-serial-core-Fix-missing-shutdown-and-startup-for-ser.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
From: Tony Lindgren <tony@atomide.com> | ||
Date: Thu, 11 Apr 2024 08:58:45 +0300 | ||
Subject: [PATCH] serial: core: Fix missing shutdown and startup for serial | ||
base port | ||
|
||
We are seeing start_tx being called after port shutdown as noted by Jiri. | ||
This happens because we are missing the startup and shutdown related | ||
functions for the serial base port. | ||
|
||
Let's fix the issue by adding startup and shutdown functions for the | ||
serial base port to block tx flushing for the serial base port when the | ||
port is not in use. | ||
|
||
Fixes: 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM") | ||
Cc: stable <stable@kernel.org> | ||
Reported-by: Jiri Slaby <jirislaby@kernel.org> | ||
Signed-off-by: Tony Lindgren <tony@atomide.com> | ||
Link: https://lore.kernel.org/r/20240411055848.38190-1-tony@atomide.com | ||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||
Origin: next-20240415, commit:1aa4ad4eb695bac1b0a7ba542a16d6833c9c8dd8 | ||
--- | ||
drivers/tty/serial/serial_base.h | 4 ++++ | ||
drivers/tty/serial/serial_core.c | 20 +++++++++++++++++--- | ||
drivers/tty/serial/serial_port.c | 34 ++++++++++++++++++++++++++++++++++ | ||
3 files changed, 55 insertions(+), 3 deletions(-) | ||
|
||
diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h | ||
index c74c548f0db6..b6c38d2edfd4 100644 | ||
--- a/drivers/tty/serial/serial_base.h | ||
+++ b/drivers/tty/serial/serial_base.h | ||
@@ -22,6 +22,7 @@ struct serial_ctrl_device { | ||
struct serial_port_device { | ||
struct device dev; | ||
struct uart_port *port; | ||
+ unsigned int tx_enabled:1; | ||
}; | ||
|
||
int serial_base_ctrl_init(void); | ||
@@ -30,6 +31,9 @@ void serial_base_ctrl_exit(void); | ||
int serial_base_port_init(void); | ||
void serial_base_port_exit(void); | ||
|
||
+void serial_base_port_startup(struct uart_port *port); | ||
+void serial_base_port_shutdown(struct uart_port *port); | ||
+ | ||
int serial_base_driver_register(struct device_driver *driver); | ||
void serial_base_driver_unregister(struct device_driver *driver); | ||
|
||
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c | ||
index 15664cda4fcd..fa9bc6f5035a 100644 | ||
--- a/drivers/tty/serial/serial_core.c | ||
+++ b/drivers/tty/serial/serial_core.c | ||
@@ -323,16 +323,26 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, | ||
bool init_hw) | ||
{ | ||
struct tty_port *port = &state->port; | ||
+ struct uart_port *uport; | ||
int retval; | ||
|
||
if (tty_port_initialized(port)) | ||
- return 0; | ||
+ goto out_base_port_startup; | ||
|
||
retval = uart_port_startup(tty, state, init_hw); | ||
- if (retval) | ||
+ if (retval) { | ||
set_bit(TTY_IO_ERROR, &tty->flags); | ||
+ return retval; | ||
+ } | ||
|
||
- return retval; | ||
+out_base_port_startup: | ||
+ uport = uart_port_check(state); | ||
+ if (!uport) | ||
+ return -EIO; | ||
+ | ||
+ serial_base_port_startup(uport); | ||
+ | ||
+ return 0; | ||
} | ||
|
||
/* | ||
@@ -355,6 +365,9 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) | ||
if (tty) | ||
set_bit(TTY_IO_ERROR, &tty->flags); | ||
|
||
+ if (uport) | ||
+ serial_base_port_shutdown(uport); | ||
+ | ||
if (tty_port_initialized(port)) { | ||
tty_port_set_initialized(port, false); | ||
|
||
@@ -1775,6 +1788,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) | ||
uport->ops->stop_rx(uport); | ||
uart_port_unlock_irq(uport); | ||
|
||
+ serial_base_port_shutdown(uport); | ||
uart_port_shutdown(port); | ||
|
||
/* | ||
diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c | ||
index 72b6f4f326e2..7d51e66ec88b 100644 | ||
--- a/drivers/tty/serial/serial_port.c | ||
+++ b/drivers/tty/serial/serial_port.c | ||
@@ -36,8 +36,12 @@ static int serial_port_runtime_resume(struct device *dev) | ||
|
||
/* Flush any pending TX for the port */ | ||
uart_port_lock_irqsave(port, &flags); | ||
+ if (!port_dev->tx_enabled) | ||
+ goto unlock; | ||
if (__serial_port_busy(port)) | ||
port->ops->start_tx(port); | ||
+ | ||
+unlock: | ||
uart_port_unlock_irqrestore(port, flags); | ||
|
||
out: | ||
@@ -57,6 +61,11 @@ static int serial_port_runtime_suspend(struct device *dev) | ||
return 0; | ||
|
||
uart_port_lock_irqsave(port, &flags); | ||
+ if (!port_dev->tx_enabled) { | ||
+ uart_port_unlock_irqrestore(port, flags); | ||
+ return 0; | ||
+ } | ||
+ | ||
busy = __serial_port_busy(port); | ||
if (busy) | ||
port->ops->start_tx(port); | ||
@@ -68,6 +77,31 @@ static int serial_port_runtime_suspend(struct device *dev) | ||
return busy ? -EBUSY : 0; | ||
} | ||
|
||
+static void serial_base_port_set_tx(struct uart_port *port, | ||
+ struct serial_port_device *port_dev, | ||
+ bool enabled) | ||
+{ | ||
+ unsigned long flags; | ||
+ | ||
+ uart_port_lock_irqsave(port, &flags); | ||
+ port_dev->tx_enabled = enabled; | ||
+ uart_port_unlock_irqrestore(port, flags); | ||
+} | ||
+ | ||
+void serial_base_port_startup(struct uart_port *port) | ||
+{ | ||
+ struct serial_port_device *port_dev = port->port_dev; | ||
+ | ||
+ serial_base_port_set_tx(port, port_dev, true); | ||
+} | ||
+ | ||
+void serial_base_port_shutdown(struct uart_port *port) | ||
+{ | ||
+ struct serial_port_device *port_dev = port->port_dev; | ||
+ | ||
+ serial_base_port_set_tx(port, port_dev, false); | ||
+} | ||
+ | ||
static DEFINE_RUNTIME_DEV_PM_OPS(serial_port_pm, | ||
serial_port_runtime_suspend, | ||
serial_port_runtime_resume, NULL); |
89 changes: 89 additions & 0 deletions
89
...ernel/linux/files/patches/0003-serial-stm32-Return-IRQ_NONE-in-the-ISR-if-no-handli.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de> | ||
Date: Wed, 17 Apr 2024 11:03:27 +0200 | ||
Subject: [PATCH] serial: stm32: Return IRQ_NONE in the ISR if no handling | ||
happend | ||
MIME-Version: 1.0 | ||
Content-Type: text/plain; charset=UTF-8 | ||
Content-Transfer-Encoding: 8bit | ||
|
||
If there is a stuck irq that the handler doesn't address, returning | ||
IRQ_HANDLED unconditionally makes it impossible for the irq core to | ||
detect the problem and disable the irq. So only return IRQ_HANDLED if | ||
an event was handled. | ||
|
||
A stuck irq is still problematic, but with this change at least it only | ||
makes the UART nonfunctional instead of occupying the (usually only) CPU | ||
by 100% and so stall the whole machine. | ||
|
||
Fixes: 48a6092fb41f ("serial: stm32-usart: Add STM32 USART Driver") | ||
Cc: stable@vger.kernel.org | ||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | ||
Forwarded: https://lore.kernel.org/r/5f92603d0dfd8a5b8014b2b10a902d91e0bb881f.1713344161.git.u.kleine-koenig@pengutronix.de | ||
--- | ||
drivers/tty/serial/stm32-usart.c | 12 ++++++++++-- | ||
1 file changed, 10 insertions(+), 2 deletions(-) | ||
|
||
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c | ||
index 693e932d6feb..7ea569c681c9 100644 | ||
--- a/drivers/tty/serial/stm32-usart.c | ||
+++ b/drivers/tty/serial/stm32-usart.c | ||
@@ -857,6 +857,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) | ||
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; | ||
u32 sr; | ||
unsigned int size; | ||
+ irqreturn_t ret = IRQ_NONE; | ||
|
||
sr = readl_relaxed(port->membase + ofs->isr); | ||
|
||
@@ -865,11 +866,14 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) | ||
(sr & USART_SR_TC)) { | ||
stm32_usart_tc_interrupt_disable(port); | ||
stm32_usart_rs485_rts_disable(port); | ||
+ ret = IRQ_HANDLED; | ||
} | ||
|
||
- if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG) | ||
+ if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG) { | ||
writel_relaxed(USART_ICR_RTOCF, | ||
port->membase + ofs->icr); | ||
+ ret = IRQ_HANDLED; | ||
+ } | ||
|
||
if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) { | ||
/* Clear wake up flag and disable wake up interrupt */ | ||
@@ -878,6 +882,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) | ||
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE); | ||
if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) | ||
pm_wakeup_event(tport->tty->dev, 0); | ||
+ ret = IRQ_HANDLED; | ||
} | ||
|
||
/* | ||
@@ -892,6 +897,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) | ||
uart_unlock_and_check_sysrq(port); | ||
if (size) | ||
tty_flip_buffer_push(tport); | ||
+ ret = IRQ_HANDLED; | ||
} | ||
} | ||
|
||
@@ -899,6 +905,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) | ||
uart_port_lock(port); | ||
stm32_usart_transmit_chars(port); | ||
uart_port_unlock(port); | ||
+ ret = IRQ_HANDLED; | ||
} | ||
|
||
/* Receiver timeout irq for DMA RX */ | ||
@@ -908,9 +915,10 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) | ||
uart_unlock_and_check_sysrq(port); | ||
if (size) | ||
tty_flip_buffer_push(tport); | ||
+ ret = IRQ_HANDLED; | ||
} | ||
|
||
- return IRQ_HANDLED; | ||
+ return ret; | ||
} | ||
|
||
static void stm32_usart_set_mctrl(struct uart_port *port, unsigned int mctrl) |
35 changes: 35 additions & 0 deletions
35
...pes-kernel/linux/files/patches/0004-serial-stm32-Reset-.throttled-state-in-.startup.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de> | ||
Date: Wed, 17 Apr 2024 11:03:28 +0200 | ||
Subject: [PATCH] serial: stm32: Reset .throttled state in .startup() | ||
MIME-Version: 1.0 | ||
Content-Type: text/plain; charset=UTF-8 | ||
Content-Transfer-Encoding: 8bit | ||
|
||
When an UART is opened that still has .throttled set from a previous | ||
open, the RX interrupt is enabled but the irq handler doesn't consider | ||
it. This easily results in a stuck irq with the effect to occupy the CPU | ||
in a tight loop. | ||
|
||
So reset the throttle state in .startup() to ensure that RX irqs are | ||
handled. | ||
|
||
Fixes: d1ec8a2eabe9 ("serial: stm32: update throttle and unthrottle ops for dma mode") | ||
Cc: stable@vger.kernel.org | ||
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | ||
Forwarded: https://lore.kernel.org/r/a784f80d3414f7db723b2ec66efc56e1ad666cbf.1713344161.git.u.kleine-koenig@pengutronix.de | ||
--- | ||
drivers/tty/serial/stm32-usart.c | 1 + | ||
1 file changed, 1 insertion(+) | ||
|
||
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c | ||
index 7ea569c681c9..d103b07d10ee 100644 | ||
--- a/drivers/tty/serial/stm32-usart.c | ||
+++ b/drivers/tty/serial/stm32-usart.c | ||
@@ -1088,6 +1088,7 @@ static int stm32_usart_startup(struct uart_port *port) | ||
val |= USART_CR2_SWAP; | ||
writel_relaxed(val, port->membase + ofs->cr2); | ||
} | ||
+ stm32_port->throttled = false; | ||
|
||
/* RX FIFO Flush */ | ||
if (ofs->rqr != UNDEF_REG) |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 5 additions & 5 deletions
10
...6.8-customers-lxa-lxatac-20240403-1.patch → ...6.8-customers-lxa-lxatac-20240417-1.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,21 @@ | ||
From: =?UTF-8?q?Leonard=20G=C3=B6hrs?= <l.goehrs@pengutronix.de> | ||
Date: Wed, 3 Apr 2024 13:59:07 +0200 | ||
Subject: [PATCH] Release 6.8/customers/lxa/lxatac/20240403-1 | ||
From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de> | ||
Date: Wed, 17 Apr 2024 11:26:49 +0200 | ||
Subject: [PATCH] Release 6.8/customers/lxa/lxatac/20240417-1 | ||
|
||
--- | ||
Makefile | 2 +- | ||
1 file changed, 1 insertion(+), 1 deletion(-) | ||
|
||
diff --git a/Makefile b/Makefile | ||
index c7ee53f4bf04..82fef008a2f2 100644 | ||
index c7ee53f4bf04..d27f84c94a5e 100644 | ||
--- a/Makefile | ||
+++ b/Makefile | ||
@@ -2,7 +2,7 @@ | ||
VERSION = 6 | ||
PATCHLEVEL = 8 | ||
SUBLEVEL = 0 | ||
-EXTRAVERSION = | ||
+EXTRAVERSION =-20240403-1 | ||
+EXTRAVERSION =-20240417-1 | ||
NAME = Hurr durr I'ma ninja sloth | ||
|
||
# *DOCUMENTATION* |
Oops, something went wrong.