diff options
author | Marek Roszko <mark.roszko@gmail.com> | 2014-01-10 04:33:11 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-01-13 18:55:59 -0500 |
commit | 8bc661bfc0c2d221e209f4205bdaaf574d50100c (patch) | |
tree | 2fbd2b8a47104ebc014476530806b1afbbe2f2f5 | |
parent | 3685f19e07802ec4207b52465c408f185b66490e (diff) |
tty/serial: at91: disable uart timer at start of shutdown
The uart timer will schedule a tasklet when it fires. It is possible that it
can fire inside _shutdown before it is killed in the dma and pdc cleanup
routines. This causes a tasklet that exists after the port is shutdown, so when
the kernel finally executes it, it panics as the tty port is NULL.
This is a somewhat rare condition but its possible if a program keeps on
opening/closing the port. It has been observed in particular with systemd
boot messages that were causing a kernel panic because of this behavior.
Moving the timer deletion to the beginning of the function stops a tasklet from
being scheduled unexpectedly.
Signed-off-by: Marek Roszko <mark.roszko@gmail.com>
Cc: stable <stable@vger.kernel.org> # v3.12
[nicolas.ferre@atmel.com: modify commit message, call setup_timer() in any case]
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/tty/serial/atmel_serial.c | 22 |
1 files changed, 10 insertions, 12 deletions
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 2b6ac1be00d3..a49f10d269b2 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c | |||
@@ -825,9 +825,6 @@ static void atmel_release_rx_dma(struct uart_port *port) | |||
825 | atmel_port->desc_rx = NULL; | 825 | atmel_port->desc_rx = NULL; |
826 | atmel_port->chan_rx = NULL; | 826 | atmel_port->chan_rx = NULL; |
827 | atmel_port->cookie_rx = -EINVAL; | 827 | atmel_port->cookie_rx = -EINVAL; |
828 | |||
829 | if (!atmel_port->is_usart) | ||
830 | del_timer_sync(&atmel_port->uart_timer); | ||
831 | } | 828 | } |
832 | 829 | ||
833 | static void atmel_rx_from_dma(struct uart_port *port) | 830 | static void atmel_rx_from_dma(struct uart_port *port) |
@@ -1229,9 +1226,6 @@ static void atmel_release_rx_pdc(struct uart_port *port) | |||
1229 | DMA_FROM_DEVICE); | 1226 | DMA_FROM_DEVICE); |
1230 | kfree(pdc->buf); | 1227 | kfree(pdc->buf); |
1231 | } | 1228 | } |
1232 | |||
1233 | if (!atmel_port->is_usart) | ||
1234 | del_timer_sync(&atmel_port->uart_timer); | ||
1235 | } | 1229 | } |
1236 | 1230 | ||
1237 | static void atmel_rx_from_pdc(struct uart_port *port) | 1231 | static void atmel_rx_from_pdc(struct uart_port *port) |
@@ -1604,12 +1598,13 @@ static int atmel_startup(struct uart_port *port) | |||
1604 | /* enable xmit & rcvr */ | 1598 | /* enable xmit & rcvr */ |
1605 | UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); | 1599 | UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); |
1606 | 1600 | ||
1601 | setup_timer(&atmel_port->uart_timer, | ||
1602 | atmel_uart_timer_callback, | ||
1603 | (unsigned long)port); | ||
1604 | |||
1607 | if (atmel_use_pdc_rx(port)) { | 1605 | if (atmel_use_pdc_rx(port)) { |
1608 | /* set UART timeout */ | 1606 | /* set UART timeout */ |
1609 | if (!atmel_port->is_usart) { | 1607 | if (!atmel_port->is_usart) { |
1610 | setup_timer(&atmel_port->uart_timer, | ||
1611 | atmel_uart_timer_callback, | ||
1612 | (unsigned long)port); | ||
1613 | mod_timer(&atmel_port->uart_timer, | 1608 | mod_timer(&atmel_port->uart_timer, |
1614 | jiffies + uart_poll_timeout(port)); | 1609 | jiffies + uart_poll_timeout(port)); |
1615 | /* set USART timeout */ | 1610 | /* set USART timeout */ |
@@ -1624,9 +1619,6 @@ static int atmel_startup(struct uart_port *port) | |||
1624 | } else if (atmel_use_dma_rx(port)) { | 1619 | } else if (atmel_use_dma_rx(port)) { |
1625 | /* set UART timeout */ | 1620 | /* set UART timeout */ |
1626 | if (!atmel_port->is_usart) { | 1621 | if (!atmel_port->is_usart) { |
1627 | setup_timer(&atmel_port->uart_timer, | ||
1628 | atmel_uart_timer_callback, | ||
1629 | (unsigned long)port); | ||
1630 | mod_timer(&atmel_port->uart_timer, | 1622 | mod_timer(&atmel_port->uart_timer, |
1631 | jiffies + uart_poll_timeout(port)); | 1623 | jiffies + uart_poll_timeout(port)); |
1632 | /* set USART timeout */ | 1624 | /* set USART timeout */ |
@@ -1652,6 +1644,12 @@ static void atmel_shutdown(struct uart_port *port) | |||
1652 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); | 1644 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); |
1653 | 1645 | ||
1654 | /* | 1646 | /* |
1647 | * Prevent any tasklets being scheduled during | ||
1648 | * cleanup | ||
1649 | */ | ||
1650 | del_timer_sync(&atmel_port->uart_timer); | ||
1651 | |||
1652 | /* | ||
1655 | * Clear out any scheduled tasklets before | 1653 | * Clear out any scheduled tasklets before |
1656 | * we destroy the buffers | 1654 | * we destroy the buffers |
1657 | */ | 1655 | */ |