On 09/07/2012 03:41 PM, Darren Hart wrote:
> commit 2588aba002d14e938c2f56d299ecf3e7ce1302a5 upstream.
>
> 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 <[email protected]>
> CC: Tomoya MORINAGA <[email protected]>
> CC: Feng Tang <[email protected]>
> CC: Alexander Stein <[email protected]>
> Acked-by: Alan Cox <[email protected]>
> Signed-off-by: Greg Kroah-Hartman <[email protected]>
> Cc: [email protected] # 3.4.x
Hi Greg,
I haven't seen anything from your scripts on this patch and I don't see
it added to the 3.4 stable-queue branch (and it didn't appear in the
recent 3.4.11).
Have I done something to break/elude the scripts?
Note that this patch should also be applied to 3.5.x.
Thanks,
Darren
>
> ---
> drivers/tty/serial/pch_uart.c | 38 ++++++++++++++++++++++++++------------
> 1 file changed, 26 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
> index c2816f4..c4d8731 100644
> --- a/drivers/tty/serial/pch_uart.c
> +++ b/drivers/tty/serial/pch_uart.c
> @@ -252,6 +252,9 @@ struct eg20t_port {
> dma_addr_t rx_buf_dma;
>
> struct dentry *debugfs;
> +
> + /* protect the eg20t_port private structure and io access to membase */
> + spinlock_t lock;
> };
>
> /**
> @@ -1056,7 +1059,7 @@ static irqreturn_t pch_uart_interrupt(int irq, void
> *dev_id)
> unsigned int iid;
> unsigned long flags;
>
> - spin_lock_irqsave(&priv->port.lock, flags);
> + spin_lock_irqsave(&priv->lock, flags);
> handled = 0;
> while ((iid = pch_uart_hal_get_iid(priv)) > 1) {
> switch (iid) {
> @@ -1107,7 +1110,7 @@ static irqreturn_t pch_uart_interrupt(int irq, void
> *dev_id)
> priv->int_dis_flag = 0;
> }
>
> - spin_unlock_irqrestore(&priv->port.lock, flags);
> + spin_unlock_irqrestore(&priv->lock, flags);
> return IRQ_RETVAL(handled);
> }
>
> @@ -1218,9 +1221,9 @@ static void pch_uart_break_ctl(struct uart_port *port,
> int ctl)
> unsigned long flags;
>
> priv = container_of(port, struct eg20t_port, port);
> - spin_lock_irqsave(&port->lock, flags);
> + spin_lock_irqsave(&priv->lock, flags);
> pch_uart_hal_set_break(priv, ctl);
> - spin_unlock_irqrestore(&port->lock, flags);
> + spin_unlock_irqrestore(&priv->lock, flags);
> }
>
> /* Grab any interrupt resources and initialise any low level driver state. */
> @@ -1368,7 +1371,8 @@ static void pch_uart_set_termios(struct uart_port *port,
>
> baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
>
> - spin_lock_irqsave(&port->lock, flags);
> + spin_lock_irqsave(&priv->lock, flags);
> + spin_lock(&port->lock);
>
> uart_update_timeout(port, termios->c_cflag, baud);
> rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb);
> @@ -1381,7 +1385,8 @@ static void pch_uart_set_termios(struct uart_port *port,
> tty_termios_encode_baud_rate(termios, baud, baud);
>
> out:
> - spin_unlock_irqrestore(&port->lock, flags);
> + spin_unlock(&port->lock);
> + spin_unlock_irqrestore(&priv->lock, flags);
> }
>
> static const char *pch_uart_type(struct uart_port *port)
> @@ -1531,8 +1536,9 @@ pch_console_write(struct console *co, const char *s,
> unsigned int count)
> {
> struct eg20t_port *priv;
> unsigned long flags;
> + int priv_locked = 1;
> + int port_locked = 1;
> u8 ier;
> - int locked = 1;
>
> priv = pch_uart_ports[co->index];
>
> @@ -1540,12 +1546,16 @@ pch_console_write(struct console *co, const char *s,
> unsigned int count)
>
> local_irq_save(flags);
> if (priv->port.sysrq) {
> - /* serial8250_handle_port() already took the lock */
> - locked = 0;
> + spin_lock(&priv->lock);
> + /* serial8250_handle_port() already took the port lock */
> + port_locked = 0;
> } else if (oops_in_progress) {
> - locked = spin_trylock(&priv->port.lock);
> - } else
> + priv_locked = spin_trylock(&priv->lock);
> + port_locked = spin_trylock(&priv->port.lock);
> + } else {
> + spin_lock(&priv->lock);
> spin_lock(&priv->port.lock);
> + }
>
> /*
> * First save the IER then disable the interrupts
> @@ -1563,8 +1573,10 @@ pch_console_write(struct console *co, const char *s,
> unsigned int count)
> wait_for_xmitr(priv, BOTH_EMPTY);
> iowrite8(ier, priv->membase + UART_IER);
>
> - if (locked)
> + if (port_locked)
> spin_unlock(&priv->port.lock);
> + if (priv_locked)
> + spin_unlock(&priv->lock);
> local_irq_restore(flags);
> }
>
> @@ -1662,6 +1674,8 @@ static struct eg20t_port *pch_uart_init_port(struct
> pci_dev *pdev,
> pci_enable_msi(pdev);
> pci_set_master(pdev);
>
> + spin_lock_init(&priv->lock);
> +
> iobase = pci_resource_start(pdev, 0);
> mapbase = pci_resource_start(pdev, 1);
> priv->mapbase = mapbase;
>
--
Darren Hart
Intel Open Source Technology Center
Yocto Project - Technical Lead - Linux Kernel
--
To unsubscribe from this list: send the line "unsubscribe stable" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html