linux/drivers/tty
Thomas Gleixner 677fe555cb serial: imx: Fix recursive locking bug
commit 9ec1882df2 (tty: serial: imx: console write routing is unsafe
on SMP) introduced a recursive locking bug in imx_console_write().

The callchain is:

imx_rxint()
  spin_lock_irqsave(&sport->port.lock,flags);
  ...
  uart_handle_sysrq_char();
    sysrq_function();
      printk();
        imx_console_write();
          spin_lock_irqsave(&sport->port.lock,flags); <--- DEAD

The bad news is that the kernel debugging facilities can dectect the
problem, but the printks never surface on the serial console for
obvious reasons.

There is a similar issue with oops_in_progress. If the kernel crashes
we really don't want to be stuck on the lock and unable to tell what
happened.

In general most UP originated drivers miss these checks and nobody
ever notices because CONFIG_PROVE_LOCKING seems to be still ignored by
a large number of developers.

The solution is to avoid locking in the sysrq case and trylock in the
oops_in_progress case.

This scheme is used in other drivers as well and it would be nice if
we could move this to a common place, so the usual copy/paste/modify
bugs can be avoided.

Now there is another issue with this scheme:

CPU0 	    	     	 CPU1
printk()
			 rxint()
			   sysrq_detection() -> sets port->sysrq
			 return from interrupt
  console_write()
     if (port->sysrq)
     	avoid locking

port->sysrq is reset with the next receive character. So as long as
the port->sysrq is not reset and this can take an endless amount of
time if after the break no futher receive character follows, all
console writes happen unlocked.

While the current writer is protected against other console writers by
the console sem, it's unprotected against open/close or other
operations which fiddle with the port. That's what the above mentioned
commit tried to solve.

That's an issue in all drivers which use that scheme and unfortunately
there is no easy workaround. The only solution is to have a separate
indicator port->sysrq_cpu. uart_handle_sysrq_char() then sets it to
smp_processor_id() before calling into handle_sysrq() and resets it to
-1 after that. Then change the locking check to:

     if (port->sysrq_cpu == smp_processor_id())
     	 locked = 0;
     else if (oops_in_progress)
         locked = spin_trylock_irqsave(port->lock, flags);
     else
  	 spin_lock_irqsave(port->lock, flags);

That would force all other cpus into the spin_lock path. Problem
solved, but that's way beyond the scope of this fix and really wants
to be implemented in a common function which calls the uart specific
write function to avoid another gazillion of hard to debug
copy/paste/modify bugs.

Reported-and-tested-by: Tim Sander <tim@krieglstein.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable <stable@vger.kernel.org>  # 3.6+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-02-14 12:17:20 -08:00
..
hvc tty: Added a CONFIG_TTY option to allow removal of TTY 2013-01-18 16:15:27 -08:00
ipwireless TTY: switch tty_flip_buffer_push 2013-01-15 22:30:15 -08:00
serial serial: imx: Fix recursive locking bug 2013-02-14 12:17:20 -08:00
vt TTY: switch tty_schedule_flip 2013-01-15 22:43:15 -08:00
amiserial.c tty: Remove ancient hardpps() 2013-02-13 10:17:06 -08:00
bfin_jtag_comm.c TTY: switch tty_flip_buffer_push 2013-01-15 22:30:15 -08:00
cyclades.c cyclades: push down tty_port_tty_get 2013-01-15 22:43:15 -08:00
ehv_bytechan.c TTY: switch tty_flip_buffer_push 2013-01-15 22:30:15 -08:00
goldfish.c goldfish: move to tty_port for flip buffers 2013-01-25 08:09:38 -08:00
isicom.c TTY: switch tty_flip_buffer_push 2013-01-15 22:30:15 -08:00
Kconfig tty: metag_da: Add metag DA TTY driver 2013-02-06 11:10:17 -08:00
Makefile tty: metag_da: Add metag DA TTY driver 2013-02-06 11:10:17 -08:00
metag_da.c tty: metag_da: avoid getting tty kref in dashtty_timer() 2013-02-06 11:10:17 -08:00
moxa.c TTY: switch tty_schedule_flip 2013-01-15 22:43:15 -08:00
moxa.h
mxser.c TTY: switch tty_flip_buffer_push 2013-01-15 22:30:15 -08:00
mxser.h
n_gsm.c tty: Prevent deadlock in n_gsm driver 2013-01-30 12:10:09 +01:00
n_hdlc.c
n_r3964.c
n_tracerouter.c
n_tracesink.c
n_tracesink.h
n_tty.c pps: Move timestamp read into PPS code proper 2013-02-13 10:13:58 -08:00
nozomi.c TTY: nozomi, remove dead code 2013-01-15 22:43:16 -08:00
pty.c pty: Ignore slave open count for master pty open 2013-02-04 15:40:29 -08:00
rocket_int.h
rocket.c tty: rocket: Explicitly list supported PCI IDs 2013-01-17 17:28:39 -08:00
rocket.h
synclink_gt.c TTY: synclink: Convert + to | for bit operations 2013-01-30 00:09:58 -05:00
synclink.c TTY: synclink: Convert + to | for bit operations 2013-01-30 00:09:58 -05:00
synclinkmp.c TTY: synclink: Convert + to | for bit operations 2013-01-30 00:09:58 -05:00
sysrq.c mm, oom: ensure sysrq+f always passes valid zonelist 2012-11-15 17:13:48 -08:00
tty_audit.c
tty_buffer.c pps: Move timestamp read into PPS code proper 2013-02-13 10:13:58 -08:00
tty_io.c tty: Fix comments that reference BKL, eventd, old paths 2013-01-15 21:57:44 -08:00
tty_ioctl.c tty: set_termios/set_termiox should not return -EINTR 2013-02-04 15:02:14 -08:00
tty_ldisc.c tty: don't deadlock while flushing workqueue 2013-01-15 23:07:15 -08:00
tty_mutex.c
tty_port.c TTY: introduce tty_port_destroy 2012-11-15 17:20:58 -08:00