[PATCH] tty: serial: meson: fix UART driver lifetime
From: Myeonghun Pak <hidden>
Date: 2026-09-13 03:41:59
Also in:
linux-amlogic, linux-arm-kernel, lkml, stable
Subsystem:
the rest, tty layer and serial drivers · Maintainers:
Linus Torvalds, Greg Kroah-Hartman, Jiri Slaby
Moving Amlogic UART registration into probe left the first driver
registration behind if uart_add_one_port() failed. Adding the separate
ttyS driver later also exposed a global last-port test: removing the last
port of one driver kept it registered whenever the other had a port.
Track whether probe registered the driver and undo it when port addition
fails. On remove, retain the current driver only if a remaining port maps
to that driver. Serialize the shared lifecycle decisions against parallel
probe and remove operations.
This issue was identified during our ongoing static-analysis research while
reviewing kernel code.
Fixes: bcb5645f99ef ("tty: serial: meson: redesign the module to platform_driver")
Fixes: e71aab9d6132 ("tty: serial: meson: apply ttyS devname instead of ttyAML for new SoCs")
Cc: stable@vger.kernel.org
Assisted-by: OpenAI:GPT-5.6
Co-developed-by: Ijae Kim <redacted>
Signed-off-by: Ijae Kim <redacted>
Signed-off-by: Myeonghun Pak <redacted>
---
drivers/tty/serial/meson_uart.c | 52 +++++++++++++++++++++++++--------
1 file changed, 40 insertions(+), 12 deletions(-)
diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c
index a6cb2a535..4bd9d9826 100644
--- a/drivers/tty/serial/meson_uart.c
+++ b/drivers/tty/serial/meson_uart.c@@ -12,6 +12,7 @@ #include <linux/io.h> #include <linux/iopoll.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/kernel.h> #include <linux/of.h> #include <linux/platform_device.h>
@@ -80,6 +81,7 @@ static struct uart_driver meson_uart_driver_ttyAML; static struct uart_driver meson_uart_driver_ttyS; static struct uart_port *meson_ports[AML_UART_PORT_NUM]; +static DEFINE_MUTEX(meson_uart_mutex); struct meson_uart_data { struct uart_driver *uart_driver;
@@ -692,6 +694,16 @@ static struct uart_driver *meson_uart_current(const struct meson_uart_data *pd) pd->uart_driver : &meson_uart_driver_ttyAML; } +static bool meson_uart_has_ports(struct uart_driver *uart_driver) +{ + for (int id = 0; id < AML_UART_PORT_NUM; id++) + if (meson_ports[id] && + meson_uart_current(meson_ports[id]->private_data) == uart_driver) + return true; + + return false; +} + static int meson_uart_probe(struct platform_device *pdev) { const struct meson_uart_data *priv_data;
@@ -702,6 +714,7 @@ static int meson_uart_probe(struct platform_device *pdev) int ret = 0; int irq; bool has_rtscts; + bool registered = false; if (pdev->dev.of_node) pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
@@ -731,11 +744,6 @@ static int meson_uart_probe(struct platform_device *pdev) of_property_read_u32(pdev->dev.of_node, "fifo-size", &fifosize); has_rtscts = of_property_read_bool(pdev->dev.of_node, "uart-has-rtscts"); - if (meson_ports[pdev->id]) { - return dev_err_probe(&pdev->dev, -EBUSY, - "port %d already allocated\n", pdev->id); - } - port = devm_kzalloc(&pdev->dev, sizeof(struct uart_port), GFP_KERNEL); if (!port) return -ENOMEM;
@@ -748,11 +756,22 @@ static int meson_uart_probe(struct platform_device *pdev) uart_driver = meson_uart_current(priv_data); + mutex_lock(&meson_uart_mutex); + + if (meson_ports[pdev->id]) { + ret = dev_err_probe(&pdev->dev, -EBUSY, + "port %d already allocated\n", pdev->id); + goto out_unlock; + } + if (!uart_driver->state) { ret = uart_register_driver(uart_driver); - if (ret) - return dev_err_probe(&pdev->dev, ret, - "can't register uart driver\n"); + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, + "can't register uart driver\n"); + goto out_unlock; + } + registered = true; } port->iotype = UPIO_MEM;
@@ -781,9 +800,14 @@ static int meson_uart_probe(struct platform_device *pdev) } ret = uart_add_one_port(uart_driver, port); - if (ret) + if (ret) { meson_ports[pdev->id] = NULL; + if (registered) + uart_unregister_driver(uart_driver); + } +out_unlock: + mutex_unlock(&meson_uart_mutex); return ret; }
@@ -794,15 +818,19 @@ static void meson_uart_remove(struct platform_device *pdev) port = platform_get_drvdata(pdev); uart_driver = meson_uart_current(port->private_data); + + mutex_lock(&meson_uart_mutex); uart_remove_one_port(uart_driver, port); meson_ports[pdev->id] = NULL; - for (int id = 0; id < AML_UART_PORT_NUM; id++) - if (meson_ports[id]) - return; + if (meson_uart_has_ports(uart_driver)) + goto out_unlock; /* No more available uart ports, unregister uart driver */ uart_unregister_driver(uart_driver); + +out_unlock: + mutex_unlock(&meson_uart_mutex); } static struct meson_uart_data meson_g12a_uart_data = {
--
2.47.1