aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/serial
diff options
context:
space:
mode:
authorAnton Vorontsov <avorontsov@ru.mvista.com>2008-07-22 06:21:07 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-22 16:03:28 -0400
commit768aec0b5bccbd460bcf6e9131f19b5a26f3862d (patch)
tree19663c2d0094b7cdce508a7d3fcb5ae072acbcdb /drivers/serial
parent88e882497d154dfb7c341902c079c9daeca1626f (diff)
serial: 8250: fix shared interrupts issues with SMP and RT kernels
With SMP kernels _irqsave spinlock disables only local interrupts, while the shared serial interrupt could be assigned to the CPU that is not currently starting up the serial port. This might cause issues because serial8250_startup() routine issues IRQ-triggering operations before registering the port in the IRQ chain (though, this is fine to do and done explicitly because we don't want to process any interrupts on the port startup). With RT kernels and preemptable hardirqs, _irqsave spinlock does not disable local hardirqs, and the bug could be reproduced much easily: $ cat /dev/ttyS0 & $ cat /dev/ttyS1 irq 42: nobody cared (try booting with the "irqpoll" option) Call Trace: [C0475EB0] [C0008A98] show_stack+0x4c/0x1ac (unreliable) [C0475EF0] [C004BBD4] __report_bad_irq+0x34/0xb8 [C0475F10] [C004BD38] note_interrupt+0xe0/0x308 [C0475F50] [C004B09C] thread_simple_irq+0xdc/0x104 [C0475F70] [C004B3FC] do_irqd+0x338/0x3c8 [C0475FC0] [C00398E0] kthread+0xf8/0x100 [C0475FF0] [C0011FE0] original_kernel_thread+0x44/0x60 handlers: [<c02112c4>] (serial8250_interrupt+0x0/0x138) Disabling IRQ #42 After this, all serial ports on the given IRQ are non-functional. To fix the issue we should explicitly disable shared IRQ before issuing any IRQ-triggering operations. I also changed spin_lock_irqsave to the ordinary spin_lock, since it seems to be safe: chain does not contain new port (yet), thus nobody will interfere us from the ISRs. Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/serial')
-rw-r--r--drivers/serial/8250.c8
1 files changed, 6 insertions, 2 deletions
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index ce948b66bbd4..27f34a9f9cb7 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -1874,7 +1874,9 @@ static int serial8250_startup(struct uart_port *port)
1874 * the interrupt is enabled. Delays are necessary to 1874 * the interrupt is enabled. Delays are necessary to
1875 * allow register changes to become visible. 1875 * allow register changes to become visible.
1876 */ 1876 */
1877 spin_lock_irqsave(&up->port.lock, flags); 1877 spin_lock(&up->port.lock);
1878 if (up->port.flags & UPF_SHARE_IRQ)
1879 disable_irq_nosync(up->port.irq);
1878 1880
1879 wait_for_xmitr(up, UART_LSR_THRE); 1881 wait_for_xmitr(up, UART_LSR_THRE);
1880 serial_out_sync(up, UART_IER, UART_IER_THRI); 1882 serial_out_sync(up, UART_IER, UART_IER_THRI);
@@ -1886,7 +1888,9 @@ static int serial8250_startup(struct uart_port *port)
1886 iir = serial_in(up, UART_IIR); 1888 iir = serial_in(up, UART_IIR);
1887 serial_out(up, UART_IER, 0); 1889 serial_out(up, UART_IER, 0);
1888 1890
1889 spin_unlock_irqrestore(&up->port.lock, flags); 1891 if (up->port.flags & UPF_SHARE_IRQ)
1892 enable_irq(up->port.irq);
1893 spin_unlock(&up->port.lock);
1890 1894
1891 /* 1895 /*
1892 * If the interrupt is not reasserted, setup a timer to 1896 * If the interrupt is not reasserted, setup a timer to