aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorDarren Hart <dvhart@linux.intel.com>2012-06-19 17:00:18 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-26 16:37:02 -0400
commit2588aba002d14e938c2f56d299ecf3e7ce1302a5 (patch)
tree872f6a087ce3615b5c8df9f75d255a4d55b713a1 /drivers/tty
parenta7b12929be6cc55eab2dac3330fa9f5984e12dda (diff)
pch_uart: Add eg20t_port lock field, avoid recursive spinlocks
pch_uart_interrupt() takes priv->port.lock which leads to two recursive spinlock calls if low_latency==1 or CONFIG_PREEMPT_RT_FULL=y (one otherwise): pch_uart_interrupt spin_lock_irqsave(priv->port.lock, flags) case PCH_UART_IID_RDR_TO (data ready) handle_rx_to push_rx tty_port_tty_get spin_lock_irqsave(&port->lock, flags) <--- already hold this lock ... tty_flip_buffer_push ... flush_to_ldisc spin_lock_irqsave(&tty->buf.lock) spin_lock_irqsave(&tty->buf.lock) disc->ops->receive_buf(tty, char_buf) n_tty_receive_buf tty->ops->flush_chars() uart_flush_chars uart_start spin_lock_irqsave(&port->lock) <--- already hold this lock Avoid this by using a dedicated lock to protect the eg20t_port structure and IO access to its membase. This is more consistent with the 8250 driver. Ensure priv->lock is always take prior to priv->port.lock when taken at the same time. V2: Remove inadvertent whitespace change. V3: Account for oops_in_progress for the private lock in pch_console_write(). Note: Like the 8250 driver, if a printk is introduced anywhere inside the pch_console_write() critical section, the kernel will hang on a recursive spinlock on the private lock. The oops case is handled by using a trylock in the oops_in_progress case. Signed-off-by: Darren Hart <dvhart@linux.intel.com> CC: Tomoya MORINAGA <tomoya.rohm@gmail.com> CC: Feng Tang <feng.tang@intel.com> CC: Alexander Stein <alexander.stein@systec-electronic.com> Acked-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/pch_uart.c38
1 files changed, 26 insertions, 12 deletions
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 4fdec6a6b758..d291518a58a4 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -253,6 +253,9 @@ struct eg20t_port {
253 dma_addr_t rx_buf_dma; 253 dma_addr_t rx_buf_dma;
254 254
255 struct dentry *debugfs; 255 struct dentry *debugfs;
256
257 /* protect the eg20t_port private structure and io access to membase */
258 spinlock_t lock;
256}; 259};
257 260
258/** 261/**
@@ -1058,7 +1061,7 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id)
1058 int next = 1; 1061 int next = 1;
1059 u8 msr; 1062 u8 msr;
1060 1063
1061 spin_lock_irqsave(&priv->port.lock, flags); 1064 spin_lock_irqsave(&priv->lock, flags);
1062 handled = 0; 1065 handled = 0;
1063 while (next) { 1066 while (next) {
1064 iid = pch_uart_hal_get_iid(priv); 1067 iid = pch_uart_hal_get_iid(priv);
@@ -1116,7 +1119,7 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id)
1116 handled |= (unsigned int)ret; 1119 handled |= (unsigned int)ret;
1117 } 1120 }
1118 1121
1119 spin_unlock_irqrestore(&priv->port.lock, flags); 1122 spin_unlock_irqrestore(&priv->lock, flags);
1120 return IRQ_RETVAL(handled); 1123 return IRQ_RETVAL(handled);
1121} 1124}
1122 1125
@@ -1226,9 +1229,9 @@ static void pch_uart_break_ctl(struct uart_port *port, int ctl)
1226 unsigned long flags; 1229 unsigned long flags;
1227 1230
1228 priv = container_of(port, struct eg20t_port, port); 1231 priv = container_of(port, struct eg20t_port, port);
1229 spin_lock_irqsave(&port->lock, flags); 1232 spin_lock_irqsave(&priv->lock, flags);
1230 pch_uart_hal_set_break(priv, ctl); 1233 pch_uart_hal_set_break(priv, ctl);
1231 spin_unlock_irqrestore(&port->lock, flags); 1234 spin_unlock_irqrestore(&priv->lock, flags);
1232} 1235}
1233 1236
1234/* Grab any interrupt resources and initialise any low level driver state. */ 1237/* Grab any interrupt resources and initialise any low level driver state. */
@@ -1376,7 +1379,8 @@ static void pch_uart_set_termios(struct uart_port *port,
1376 1379
1377 baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); 1380 baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
1378 1381
1379 spin_lock_irqsave(&port->lock, flags); 1382 spin_lock_irqsave(&priv->lock, flags);
1383 spin_lock(&port->lock);
1380 1384
1381 uart_update_timeout(port, termios->c_cflag, baud); 1385 uart_update_timeout(port, termios->c_cflag, baud);
1382 rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb); 1386 rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb);
@@ -1389,7 +1393,8 @@ static void pch_uart_set_termios(struct uart_port *port,
1389 tty_termios_encode_baud_rate(termios, baud, baud); 1393 tty_termios_encode_baud_rate(termios, baud, baud);
1390 1394
1391out: 1395out:
1392 spin_unlock_irqrestore(&port->lock, flags); 1396 spin_unlock(&port->lock);
1397 spin_unlock_irqrestore(&priv->lock, flags);
1393} 1398}
1394 1399
1395static const char *pch_uart_type(struct uart_port *port) 1400static const char *pch_uart_type(struct uart_port *port)
@@ -1538,8 +1543,9 @@ pch_console_write(struct console *co, const char *s, unsigned int count)
1538{ 1543{
1539 struct eg20t_port *priv; 1544 struct eg20t_port *priv;
1540 unsigned long flags; 1545 unsigned long flags;
1546 int priv_locked = 1;
1547 int port_locked = 1;
1541 u8 ier; 1548 u8 ier;
1542 int locked = 1;
1543 1549
1544 priv = pch_uart_ports[co->index]; 1550 priv = pch_uart_ports[co->index];
1545 1551
@@ -1547,12 +1553,16 @@ pch_console_write(struct console *co, const char *s, unsigned int count)
1547 1553
1548 local_irq_save(flags); 1554 local_irq_save(flags);
1549 if (priv->port.sysrq) { 1555 if (priv->port.sysrq) {
1550 /* serial8250_handle_port() already took the lock */ 1556 spin_lock(&priv->lock);
1551 locked = 0; 1557 /* serial8250_handle_port() already took the port lock */
1558 port_locked = 0;
1552 } else if (oops_in_progress) { 1559 } else if (oops_in_progress) {
1553 locked = spin_trylock(&priv->port.lock); 1560 priv_locked = spin_trylock(&priv->lock);
1554 } else 1561 port_locked = spin_trylock(&priv->port.lock);
1562 } else {
1563 spin_lock(&priv->lock);
1555 spin_lock(&priv->port.lock); 1564 spin_lock(&priv->port.lock);
1565 }
1556 1566
1557 /* 1567 /*
1558 * First save the IER then disable the interrupts 1568 * First save the IER then disable the interrupts
@@ -1570,8 +1580,10 @@ pch_console_write(struct console *co, const char *s, unsigned int count)
1570 wait_for_xmitr(priv, BOTH_EMPTY); 1580 wait_for_xmitr(priv, BOTH_EMPTY);
1571 iowrite8(ier, priv->membase + UART_IER); 1581 iowrite8(ier, priv->membase + UART_IER);
1572 1582
1573 if (locked) 1583 if (port_locked)
1574 spin_unlock(&priv->port.lock); 1584 spin_unlock(&priv->port.lock);
1585 if (priv_locked)
1586 spin_unlock(&priv->lock);
1575 local_irq_restore(flags); 1587 local_irq_restore(flags);
1576} 1588}
1577 1589
@@ -1669,6 +1681,8 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
1669 pci_enable_msi(pdev); 1681 pci_enable_msi(pdev);
1670 pci_set_master(pdev); 1682 pci_set_master(pdev);
1671 1683
1684 spin_lock_init(&priv->lock);
1685
1672 iobase = pci_resource_start(pdev, 0); 1686 iobase = pci_resource_start(pdev, 0);
1673 mapbase = pci_resource_start(pdev, 1); 1687 mapbase = pci_resource_start(pdev, 1);
1674 priv->mapbase = mapbase; 1688 priv->mapbase = mapbase;