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 | */ |
