tty/serial: at91: fixes dealing with port shutdown
Subsystems (4)
cover tty/serial: at91: fixes dealing with port shutdown
Hi,
These four fixes are pretty important for our atmel_serial driver as they deal with closing/re-opening of ports. They fix race condition and null pointers dereference.
I added the "stable" tag to each of them (v3.12).
Even if we are late in the development cycle, can you please consider including these patches for 3.13?
Thanks, best regards,
Nicolas Ferre
Marek Roszko (3):
tty/serial: at91: Handle shutdown more safely tty/serial: at91: fix race condition in atmel_serial_remove tty/serial: at91: prevent null dereference in tasklet function
Mark Deneen (1):
tty/serial: at91: reset rx_ring when port is shutdown
drivers/tty/serial/atmel_serial.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-)
Signature · 1 line
1.8.2.2
PATCH 1/4 tty/serial: at91: Handle shutdown more safely
From: Marek Roszko <[email protected]>
Interrupts were being cleaned up late in the shutdown handler, it is possible that an interrupt can occur and schedule a tasklet that runs after the port is cleaned up. There is a null dereference due to this race condition with the following stacktrace:
[<c02092b0>] (atmel_tasklet_func+0x514/0x814) from [<c001fd34>] (tasklet_action+0x70/0xa8) [<c001fd34>] (tasklet_action+0x70/0xa8) from [<c001f60c>] (__do_softirq+0x90/0x144) [<c001f60c>] (__do_softirq+0x90/0x144) from [<c001fa18>] (irq_exit+0x40/0x4c) [<c001fa18>] (irq_exit+0x40/0x4c) from [<c000e298>] (handle_IRQ+0x64/0x84) [<c000e298>] (handle_IRQ+0x64/0x84) from [<c000d6c0>] (__irq_svc+0x40/0x50) [<c000d6c0>] (__irq_svc+0x40/0x50) from [<c0208060>] (atmel_rx_dma_release+0x88/0xb8) [<c0208060>] (atmel_rx_dma_release+0x88/0xb8) from [<c0209740>] (atmel_shutdown+0x104/0x160) [<c0209740>] (atmel_shutdown+0x104/0x160) from [<c0205e8c>] (uart_port_shutdown+0x2c/0x38)
--- drivers/tty/serial/atmel_serial.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
drivers/tty/serial/atmel_serial.c +13 -7
@@ -1650,12 +1650,24 @@ static int atmel_startup(struct uart_port *port)static void atmel_shutdown (struct uart_port *port) {struct atmel_uart_port *atmel_port =to_atmel_uart_port (port); +/* -* Ensure everything is stopped. +* Clear out any scheduled tasklets before +* we destroy the buffers +*/ +tasklet_kill (&atmel_port->tasklet); + +/* +* Ensure everything is stopped and +* disable all interrupts, port and break condition. */ atmel_stop_rx (port);atmel_stop_tx (port); +UART_PUT_CR (port,ATMEL_US_RSTSTA ); +UART_PUT_IDR (port,-1 ); + +/* * Shut-down the DMA. */
@@ -1665,12 +1677,6 @@ static void atmel_shutdown(struct uart_port *port) atmel_port->release_tx (port);/* -* Disable all interrupts, port and break condition. -*/ -UART_PUT_CR (port,ATMEL_US_RSTSTA ); -UART_PUT_IDR (port,-1 ); - -/* * Free the interrupt */ free_irq (port->irq, port);
Signature · 1 line
1.8.2.2
PATCH 2/4 tty/serial: at91: fix race condition in atmel_serial_remove
In reply to Nicolas FerreFrom: Marek Roszko <[email protected]>
The _remove callback could be called when a tasklet is scheduled. tasklet_kill was called inside the function in order to free up any scheduled tasklets. However it was called after uart_remove_one_port which destroys tty references needed in the port for atmel_tasklet_func.
Simply putting the tasklet_kill at the start of the function will prevent this conflict.
--- drivers/tty/serial/atmel_serial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/tty/serial/atmel_serial.c +2 -1
@@ -2447,11 +2447,12 @@ static int atmel_serial_remove(struct platform_device *pdev)struct atmel_uart_port *atmel_port =to_atmel_uart_port (port);int ret =0 ; +tasklet_kill (&atmel_port->tasklet); +device_init_wakeup (&pdev->dev,0 ); ret =uart_remove_one_port (&atmel_uart, port); -tasklet_kill (&atmel_port->tasklet);kfree (atmel_port->rx_ring.buf);/* "port" is allocated statically, so we shouldn't free it */
Signature · 1 line
1.8.2.2
PATCH 3/4 tty/serial: at91: prevent null dereference in tasklet function
In reply to Nicolas FerreFrom: Marek Roszko <[email protected]>
Something asks a tasklet to be scheduled when the uart port is closed. Need to supress the kernel panic for now by checking if the port is NULL or not.
--- drivers/tty/serial/atmel_serial.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/tty/serial/atmel_serial.c +4 -0
@@ -1360,6 +1360,10 @@ static void atmel_tasklet_func(unsigned long data)unsigned int status;unsigned int status_change; +if (!port->state || !port->state->port.tty) +/* uart has been closed */ +return ; +/* The interrupt handler does not take the lock */ spin_lock (&port->lock);
Signature · 1 line
1.8.2.2
Greg KH
maintainerRe: [PATCH 3/4] tty/serial: at91: prevent null dereference in tasklet function
Quoted 3 lines · Nicolas Ferre
From: Marek Roszko <[email protected]>
Something asks a tasklet to be scheduled when the uart port is closed.
What is that something? Shouldn't you track that down and find the real problem here?
Quoted 16 lines
Need to supress the kernel panic for now by checking if the port is NULL or not.
Signed-off-by Marek Roszko <[email protected]>Acked-by Leilei Zhao <[email protected]>Cc <[email protected]> # v3.12Signed-off-by Nicolas Ferre <[email protected]>--- drivers/tty/serial/atmel_serial.c | 4 ++++ 1 file changed, 4 insertions(+)drivers/tty/serial/atmel_serial.c +0
-0
drivers/tty/serial/atmel_serial.c static void atmel_tasklet_func(unsigned long data) · line 1365
Quoted 6 lines
drivers/tty/serial/atmel_serial.c +3
-0@@ -1360,6 +1360,10 @@ static void atmel_tasklet_func(unsigned long data)unsigned int status;unsigned int status_change; +if (!port->state || !port->state->port.tty) +/* uart has been closed */ +return ;
Did you really test this?
How about running it through checkpatch?
thanks,
greg k-h
A message that is not in the archive (sent off-list or not yet received): CAJjB1qLDd6JFb4LwD5iokg5=O8Bwp5MkKsrr45QDodZkZrx75g@mail.gmail.com
Greg KH
maintainerRe: [PATCH 3/4] tty/serial: at91: prevent null dereference in tasklet function
A: No.
Q: Should I include quotations after my reply?
http://daringfireball.net/2007/07/on_top
Quoted 2 lines · Mark Roszko
This patch I was somewhat hesitant to pushing to Atmel when I did the other two patches.
Then why did you? :)
Quoted 5 lines
The main issue is the use of systemd causes the serial driver to somehow get out of sync on startups randomly. i.e. on one bootup it's fine, and on other it'll kernel panic.
It occurs because systemd causes the startup and shutdown driver ops to be fired in rapid succession.
How does systemd cause this? Is this when the serial port is being used as a console or something else?
Quoted 2 lines
Every single service start message causes the
_startupcallback first, then the message prints and_shutdowncallbacks fires.
So something is opening and closing the port quickly? That should be easy to test without systemd.
Any console involved?
Quoted 11 lines
And a kernel panic always occurs somewhere during the service status output, never before when it's just the kernel startup and never after once systemd finishes and getty for example takes over.
The stacktrace looked like this:
Unable to handle kernel NULL pointer dereference at virtual address 00000088 pgd = c0004000 [00000088] *pgd=00000000 Internal error: Oops: 17 [#1] ARM Modules linked in: autofs4 CPU: 0 Not tainted (3.6.9-yocto-standard #1)
3.6.9 is _very_ old, loads of things have happened in the tty layer since then, can you duplicate this on 3.12 or 3.13-rc?
Quoted 28 lines
PC is at
tty_wakeup+0x8/0x58
LR is atatmel_tasklet_func+0x238/0x80cpc : [<c01efd84>] lr : [<c0208fc0>] psr: a00f0013sp :
df84ff28ip : 00000000 fp : 00000100
r10:c05d1ec0r9 : 04208040 r8 :c05d1e80
r7 :0000000ar6 : 00000000 r5 :dedf7c00r4 : 00000000 r3 :dedf7c00r2 : 00000000 r1 :600f0013r0 : 00000000
Flags: NzCv IRQs on FIQs on ModeSVC_32ISA ARM Segment kernel
Control:10c53c7dTable:3fb0c059DAC: 00000015 Process ksoftirqd/0 (pid: 3, stack limit =0xdf84e2f0)Stack: (0xdf84ff28 to 0xdf850000)ff20:
c05e4378dedf7c0000000000c0208fc0c05aa458df8496b0
ff40:df849680c05aa458df8496b0c00394a8c0039420c05a8d0400000000 00000000
ff60:0000000ac05d1e8004208040c05d1ec000000100c001fd3400000009 00000018
ff80:df84e000c001f60cdf84ffbcc03f8e6000000013 00000006 00000000c05d1e80
ffa0:df84e00000000000 00000013 00000000 00000000 00000000 00000000c001f728
ffc0:df84bf3c00000000c001f6c0c003087000000000 00000000 00000000 00000000
ffe0:df84ffe0df84ffe0df84bf3cc00307f0c000e348c000e348fff73fbf3fbeffff[<c01efd84>] (tty_wakeup+0x8/0x58) from [<c0208fc0>] (atmel_tasklet_func+0x238/0x80c)
[<c0208fc0>] (atmel_tasklet_func+0x238/0x80c) from [<c001fd34>] (tasklet_action+0x70/0xa8)
[<c001fd34>] (tasklet_action+0x70/0xa8) from [<c001f60c>] (__do_softirq+0x90/0x144)
[<c001f60c>] (__do_softirq+0x90/0x144) from [<c001f728>] (run_ksoftirqd+0x68/0x104)
[<c001f728>] (run_ksoftirqd+0x68/0x104) from [<c0030870>] (kthread+0x80/0x90) [<c0030870>] (kthread+0x80/0x90) from [<c000e348>] (kernel_thread_exit+0x0/0x8)
Quoted 7 lines
Code: e8bd8070 c05ac0b8 e92d4070 e1a04000 (e5903088) ---[ end trace 15b8a9aeaf7b457f ]---Testing wise I originally used a
BUG_ONstatement inatmel_tasklet_functo panic before the null deference hit.BUG_ONconfirmed that tty was NULL at the very start of the tasklet being called.
And did you test that this patch actually fixed it?
Quoted 4 lines
The
atmel_shutdownfunction should be killing the tasklet (after patch "Handle shutdown more safely") and does disable interrupts so I've been at a loss at where the race condition was occurring that a tasklet could escape beyond shutdown.
Are you sure you aren't just racing with shutdown? Need a lock somewhere?
greg k-h
PATCH 4/4 tty/serial: at91: reset rx_ring when port is shutdown
In reply to Nicolas FerreFrom: Mark Deneen <[email protected]>
When using RX DMA, the driver won't pass any data to the uart layer until the buffer is flipped. When the port is shutdown, the dma buffers are unmapped, but the head and tail of the ring buffer are not reseted. Since the serial console will keep the port open, this will only present itself when the uart is not shared.
To reproduce the issue, with an unpatched driver, run a getty on /dev/ttyS0 with no serial console and exit. Getty will exit, and when the new one returns you will be unable to log in. If you hold down a key long enough to fill the DMA buffer and flip it, you can then log in.
--- drivers/tty/serial/atmel_serial.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/tty/serial/atmel_serial.c +6 -0
@@ -1681,6 +1681,12 @@ static void atmel_shutdown(struct uart_port *port) atmel_port->release_tx (port);/* +* Reset ring buffer pointers +*/ + atmel_port->rx_ring.head =0 ; + atmel_port->rx_ring.tail =0 ; + +/* * Free the interrupt */ free_irq (port->irq, port);
Signature · 1 line
1.8.2.2
On 07/01/2014 11:41, Nicolas Ferre :
Quoted 1 line
Hi,
Sorry for the noise: I messed up with git-send-email...
I re-sent the whole series.
Bye,
Quoted 22 lines
These four fixes are pretty important for our
atmel_serialdriver as they deal with closing/re-opening of ports. They fix race condition and null pointers dereference.
I added the "stable" tag to each of them (v3.12).Even if we are late in the development cycle, can you please consider including these patches for 3.13?
Thanks, best regards,
Nicolas FerreMarek Roszko (3):
tty/serial: at91: Handle shutdown more safely tty/serial: at91: fix race condition inatmel_serial_removetty/serial: at91: prevent null dereference in tasklet functionMark Deneen (1):
tty/serial: at91: resetrx_ringwhen port is shutdowndrivers/tty/serial/atmel_serial.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-)
Signature · 1 line
Nicolas Ferre