summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2022-02-15 19:02:36 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-04-08 15:23:46 +0300
commit3e843460fb4252368bef6c9c8897a40a1268d5a0 (patch)
treeffa730f06f461e645c6609d3e5c956308194a61e /drivers/tty
parentba3a3390c9b17abd71ac44f4a8f7647f3e4e01fc (diff)
downloadlinux-3e843460fb4252368bef6c9c8897a40a1268d5a0.tar.xz
serial: 8250: Fix race condition in RTS-after-send handling
[ Upstream commit dedab69fd650ea74710b2e626e63fd35584ef773 ] Set em485->active_timer = NULL isn't always enough to take out the stop timer. While there is a check that it acts in the right state (i.e. waiting for RTS-after-send to pass after sending some chars) but the following might happen: - CPU1: some chars send, shifter becomes empty, stop tx timer armed - CPU0: more chars send before RTS-after-send expired - CPU0: shifter empty irq, port lock taken - CPU1: tx timer triggers, waits for port lock - CPU0: em485->active_timer = &em485->stop_tx_timer, hrtimer_start(), releases lock() - CPU1: get lock, see em485->active_timer == &em485->stop_tx_timer, tear down RTS too early This fix bases on research done by Steffen Trumtrar. Fixes: b86f86e8e7c5 ("serial: 8250: fix potential deadlock in rs485-mode") Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Link: https://lore.kernel.org/r/20220215160236.344236-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/8250/8250_port.c12
1 files changed, 12 insertions, 0 deletions
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index b470bc747b99..868ccb3e16cf 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1623,6 +1623,18 @@ static inline void start_tx_rs485(struct uart_port *port)
struct uart_8250_port *up = up_to_u8250p(port);
struct uart_8250_em485 *em485 = up->em485;
+ /*
+ * While serial8250_em485_handle_stop_tx() is a noop if
+ * em485->active_timer != &em485->stop_tx_timer, it might happen that
+ * the timer is still armed and triggers only after the current bunch of
+ * chars is send and em485->active_timer == &em485->stop_tx_timer again.
+ * So cancel the timer. There is still a theoretical race condition if
+ * the timer is already running and only comes around to check for
+ * em485->active_timer when &em485->stop_tx_timer is armed again.
+ */
+ if (em485->active_timer == &em485->stop_tx_timer)
+ hrtimer_try_to_cancel(&em485->stop_tx_timer);
+
em485->active_timer = NULL;
if (em485->tx_stopped) {