summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/sirfsoc_uart.c
diff options
context:
space:
mode:
authorQipan Li <Qipan.Li@csr.com>2014-01-27 10:23:39 +0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-02-13 22:02:19 +0400
commitfb78b811422cd2d8c8605949cc4cc13618347ad5 (patch)
tree426d743f1abac0cf8d43d19f1b97a2f9d5acff9f /drivers/tty/serial/sirfsoc_uart.c
parent333c085e4b0cd9df04b087d29f87969a4e26dfa9 (diff)
downloadlinux-fb78b811422cd2d8c8605949cc4cc13618347ad5.tar.xz
serial: sirf: fix kernel panic caused by unpaired spinlock
commit 8b9ade9f74f8a279 coming from Viresh Kumar "tty: serial: sirfsoc: drop uart_port->lock before calling tty_flip_buffer_push()" broke sirfsoc uart driver by knic: [ 5.129122] BUG: spinlock already unlocked on CPU#0, ip6tables/1331 [ 5.132554] lock: sirfsoc_uart_ports+0x4/0x8a0, .magic: dead4ead, .owner: <none>/-1, .owner_cpu: -1 [ 5.141651] CPU: 0 PID: 1331 Comm: ip6tables Tainted: G W O 3.10.16 #3 [ 5.148866] [<c0013528>] (unwind_backtrace+0x0/0xe0) from [<c0010e70>] (show_stack+0x10/0x14) [ 5.157362] [<c0010e70>] (show_stack+0x10/0x14) from [<c01a5e68>] (do_raw_spin_unlock+0x40/0xc8) [ 5.166125] [<c01a5e68>] (do_raw_spin_unlock+0x40/0xc8) from [<c03ff8b4>] (_raw_spin_unlock+0x8/0x40) [ 5.175322] [<c03ff8b4>] (_raw_spin_unlock+0x8/0x40) from [<c0203fcc>] (sirfsoc_uart_pio_rx_chars+0xa4/0xc0) [ 5.185120] [<c0203fcc>] (sirfsoc_uart_pio_rx_chars+0xa4/0xc0) from [<c0204fb8>] (sirfsoc_rx_tmo_process_tl+0xdc/0x1e0) [ 5.195875] [<c0204fb8>] (sirfsoc_rx_tmo_process_tl+0xdc/0x1e0) from [<c0024b50>] (tasklet_action+0x8c/0xec) [ 5.205673] [<c0024b50>] (tasklet_action+0x8c/0xec) from [<c00242a8>] (__do_softirq+0xec/0x1d4) [ 5.214347] [<c00242a8>] (__do_softirq+0xec/0x1d4) from [<c0024428>] (do_softirq+0x48/0x54) [ 5.222674] [<c0024428>] (do_softirq+0x48/0x54) from [<c0024690>] (irq_exit+0x74/0xc0) [ 5.230573] [<c0024690>] (irq_exit+0x74/0xc0) from [<c000e1e8>] (handle_IRQ+0x6c/0x90) [ 5.238465] [<c000e1e8>] (handle_IRQ+0x6c/0x90) from [<c000d500>] (__irq_svc+0x40/0x70) [ 5.246446] [<c000d500>] (__irq_svc+0x40/0x70) from [<c0092e7c>] (mark_page_accessed+0xc/0x68) [ 5.255034] [<c0092e7c>] (mark_page_accessed+0xc/0x68) from [<c00a2a4c>] (unmap_single_vma+0x3bc/0x550) [ 5.264402] [<c00a2a4c>] (unmap_single_vma+0x3bc/0x550) from [<c00a3b4c>] (unmap_vmas+0x44/0x54) [ 5.273164] [<c00a3b4c>] (unmap_vmas+0x44/0x54) from [<c00a81a8>] (exit_mmap+0xc4/0x1e0) [ 5.281233] [<c00a81a8>] (exit_mmap+0xc4/0x1e0) from [<c001bb78>] (mmput+0x3c/0xdc) [ 5.288868] [<c001bb78>] (mmput+0x3c/0xdc) from [<c0021b0c>] (do_exit+0x30c/0x828) [ 5.296413] [<c0021b0c>] (do_exit+0x30c/0x828) from [<c0022dac>] (do_group_exit+0x4c/0xb0) [ 5.304653] [<c0022dac>] (do_group_exit+0x4c/0xb0) from [<c0022e20>] (__wake_up_parent+0x0/0x18) Root cause: the commit dropped uart_port->lock before calling tty_flip_buffer_push(), but in sirfsoc-uart, sirfsoc_uart_pio_rx_chars() can be called by sirfsoc_rx_tmo_process_tl(). here uart_port->lock has not been taken yet. so that caused unpaired lock/unlock. Solution: This patch is doing a quick fix for that, it adds spin_lock/unlock(&port->lock) protect to sirfsoc_uart_pio_rx_chars() in sirfsoc_rx_tmo_process_tl() to keep spin_lock/unlock in pair. Signed-off-by: Qipan Li <Qipan.Li@csr.com> Signed-off-by: Barry Song <Baohua.Song@csr.com> Cc: stable <stable@vger.kernel.org> # 3.12 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial/sirfsoc_uart.c')
-rw-r--r--drivers/tty/serial/sirfsoc_uart.c4
1 files changed, 3 insertions, 1 deletions
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index 49a2ffd101a7..b7bfe24d4ebc 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -542,8 +542,10 @@ static void sirfsoc_rx_tmo_process_tl(unsigned long param)
wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) |
SIRFUART_IO_MODE);
- sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count);
spin_unlock_irqrestore(&sirfport->rx_lock, flags);
+ spin_lock(&port->lock);
+ sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count);
+ spin_unlock(&port->lock);
if (sirfport->rx_io_count == 4) {
spin_lock_irqsave(&sirfport->rx_lock, flags);
sirfport->rx_io_count = 0;