From d208a3bf77f902283894f546b6b5383202cf7882 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 19 Oct 2011 11:52:01 -0700 Subject: [PATCH] TTY: serial_core: Fix crash if DCD drop during suspend This crash was showing up 100% of the time on Tegra CPUs when an agetty was running on the serial port and the console was not running on the serial port. The reason the Tegra saw it so reliably is that the Tegra CPU internally ties DTR to DCD/DSR. That means when we dropped DTR during suspend we would get always get an immediate DCD drop. The specific order of operations that were running: * uart_suspend_port() would be called to put the uart in suspend mode * we'd drop DTR (ops->set_mctrl(uport, 0)). * the DTR drop would be looped back in the CPU to be a DCD drop. * the DCD drop would look to the serial driver as a hangup * the hangup would call uart_shutdown() * ... suspend / resume happens ... * uart_resume_port() would be called and run the code in the (port->flags & ASYNC_SUSPENDED) block, which would startup the port (and enable tx again). * Since the UART would be available for tx, we'd immediately get an interrupt, eventually calling transmit_chars() * The transmit_chars() function would crash. The first crash would be a dereference of a NULL tty member, but since the port has been shutdown that was just a symptom. I have proposed a patch that would fix the Tegra CPUs here (see https://lkml.org/lkml/2011/10/11/444 - tty/serial: Prevent drop of DCD on suspend for Tegra UARTs). However, even with that fix it is still possible for systems that have an externally visible DCD line to see a crash if the DCD drops at just the right time during suspend: thus this patch is still useful. Signed-off-by: Doug Anderson Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index e562b1224466..9f72be28e6f2 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -243,6 +243,13 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) synchronize_irq(uport->irq); } + /* + * It's possible for shutdown to be called after suspend if we get + * a DCD drop (hangup) at just the right time. Clear suspended bit so + * we don't try to resume a port that has been shutdown. + */ + clear_bit(ASYNCB_SUSPENDED, &port->flags); + /* * Free the transmit buffer page. */