linux4sam/linux-at91 — pull request #4 by marekr (2013-12-20 02:39:10+00:00)
Title: Serial fixes

This pull request helps fix some of the issues when running the kernel with "systemd" as the init manager. Turns out atmel_serial really cannot handle it's behavior without race conditions causing kernel panics on boot.

The source of grief seems to be systemd will cause the driver to fire the startup and shutdown uart ops constantly for every single line it prints, i.e.

ATMEL_STARTUP
[OK] Some Service
ATMEL_SHUTDOWN
ATMEL_STARTUP
[OK] Another Service
ATMEL_SHUTDOWN

Thus an kernel panic tends to occur anytime its printing its services list, no particular service message causes it to fail.

Once systemd finished starting services, the panics won't occur, one last STARTUP is called when a program like getty launches to take over the port but no spam of the STARTUP/SHUTDOWN funcs afterwards.

There are 2 panics that can occur, one is interrupts occuring in atmel_shutdown after the buffers were destroyed but before the interrupts were fully disabled:
[] (atmel_tasklet_func+0x514/0x814) from [] (tasklet_action+0x70/0xa8)
[] (tasklet_action+0x70/0xa8) from [] (__do_softirq+0x90/0x144)
[] (__do_softirq+0x90/0x144) from [] (irq_exit+0x40/0x4c)
[] (irq_exit+0x40/0x4c) from [] (handle_IRQ+0x64/0x84)
[] (handle_IRQ+0x64/0x84) from [] (__irq_svc+0x40/0x50)
[] (__irq_svc+0x40/0x50) from [] (atmel_rx_dma_release+0x88/0xb8)
[] (atmel_rx_dma_release+0x88/0xb8) from [] (atmel_shutdown+0x104/0x160)
[] (atmel_shutdown+0x104/0x160) from [] (uart_port_shutdown+0x2c/0x38)
[] (uart_port_shutdown+0x2c/0x38) from [] (uart_shutdown+0x9c/0xd4)
[] (uart_shutdown+0x9c/0xd4) from [] (uart_close+0x7c/0x174)
[] (uart_close+0x7c/0x174) from [] (tty_release+0x154/0x428)
[] (tty_release+0x154/0x428) from [] (__fput+0xe4/0x1e0)
[] (__fput+0xe4/0x1e0) from [] (task_work_run+0x48/0x5c)
[] (task_work_run+0x48/0x5c) from [] (do_work_pending+0x90/0xa4)
[] (do_work_pending+0x90/0xa4) from [] (work_pending+0xc/0x20)

This is solved by simply killing all interrupts at the start of _shutdown and also calling tasklet_kill to make sure nothing is scheduled that could run later.

And the second which has been difficult to track down and driven me insane:
[] (tty_wakeup+0x8/0x58) from [] (atmel_tasklet_func+0x238/0x80c)
[] (atmel_tasklet_func+0x238/0x80c) from [] (tasklet_action+0x70/0xa8)
[] (tasklet_action+0x70/0xa8) from [] (__do_softirq+0x90/0x144)
[] (__do_softirq+0x90/0x144) from [] (run_ksoftirqd+0x68/0x104)
[] (run_ksoftirqd+0x68/0x104) from [] (kthread+0x80/0x90)
[] (kthread+0x80/0x90) from [] (kernel_thread_exit+0x0/0x8)

For some reason atmel_tasklet_func gets scheduled when tty/uart is closed and the tasklet isn't killed. I haven't found what code path could cause this.

Unforunately the solution to the second panic is just to return on NULL tty at the start of the tasklet.

Lastly, there is a conflict possible(but I haven't seen) in the atmel_serial_remove function where the tasklet_kill was called after uart_remove_one_port, if a tasklet was scheduled, then it would fire on the kill and proceed to kernel panic on null dereference because the tty/port references were nulled by the first function. Flipping the order of functions should be safer.