Re: Regression in dbdda842fe96 ("printk: Add console owner and waiter logic to load balance console writes") [Was: Regression in fd5f7cde1b85 ("...")]

From: Petr Mladek
Date: Thu Sep 26 2019 - 04:59:02 EST


On Wed 2019-09-18 16:52:52, Sergey Senozhatsky wrote:
> On (09/18/19 09:11), Uwe Kleine-König wrote:
> > I rechecked and indeed fd5f7cde1b85's parent has the problem, too, so I
> > did a mistake during my bisection :-|
> >
> > Redoing the bisection (a bit quicker this time) points to
> >
> > dbdda842fe96 ("printk: Add console owner and waiter logic to load balance console writes")
> >
> > Sorry for the confusion.
>
> No worries!
>
> [..]
> > > So I'd say that lockdep is correct, but there are several hacks which
> > > prevent actual deadlock.
>
> The basic idea is to handle sysrq out of port->lock.

Great idea!

> I didn't test it all (not even sure if it compiles).
>
> ---
> drivers/tty/serial/imx.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index 87c58f9f6390..f0dd807b52df 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -731,9 +731,9 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
> struct imx_port *sport = dev_id;
> unsigned int rx, flg, ignored = 0;
> struct tty_port *port = &sport->port.state->port;
> + unsigned long flags;
>
> - spin_lock(&sport->port.lock);
> -
> + uart_port_lock_irqsave(&sport->port, flags);

uart_port_lock_irqsave() does not exist. Instead the current users
do:

spin_lock_irqsave(&port->lock, flags);

> while (imx_uart_readl(sport, USR2) & USR2_RDR) {
> u32 usr2;
>
> @@ -749,8 +749,8 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
> continue;
> }
>
> - if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
> - continue;
> + if (uart_prepare_sysrq_char(&sport->port, (unsigned char)rx))
> + break;
>
> if (unlikely(rx & URXD_ERR)) {
> if (rx & URXD_BRK)
> @@ -792,7 +792,7 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
> }
>
> out:
> - spin_unlock(&sport->port.lock);
> + uart_unlock_and_check_sysrq(&sport->port, flags);

This API has been introduced for exactly this reason. See the commit
6e1935819db0c91ce4a5af ("serial: core: Allow processing sysrq at port
unlock time").

I like this approach. It allows to remove hacks with locks.

Well, Sergey's patch is nice example that the API is a bit confusing.
I would either make it symmetric and make a variant without saving
irq flags:

uart_lock(port);
uart_unlock_and_handle_sysrq(port);

uart_lock_irqsave(port, flags);
uart_unlock_irqrestore_and_handle_sysrq(port);

Or I would keep the locking as is and add some API
just for the sysrq handling:


int uart_store_sysrq_char(struct uart_port *port, unsigned int ch);
unsigned int uart_get_sysrq_char(struct uart_port *port);

And use it the following way:

int handle_irq()
{
unsined int sysrq, sysrq_ch;

spin_lock(&port->lock);
[...]
sysrq = uart_store_sysrq_char(port, ch);
if (!sysrq)
[...]
[...]

out:
sysrq_ch = uart_get_sysrq_char(port);
spin_unlock(&port->lock);

if (sysrq_ch)
handle_sysrq(sysrq_ch);
}

I prefer the 2nd option. It is more code. But it is more
self explanatory.

What do you think?

Best Regards,
Petr