Re: [PATCH] tty: serial: imx: fix potential deadlock

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Nov 09, 2020 at 06:54:55AM +1300, Sam Nobs wrote:
> Enabling the lock dependency validator has revealed
> that the way spinlocks are used in the IMX serial
> port could result in a deadlock. Note that the configuration
> I'm using has the IRQs forced to run in threads via
> the kernel parameter threadirqs.
> 
> Specifically, imx_uart_int() acquires a spinlock
> without disabling the interrupts, meaning that another
> interrupt could come along and try to acquire the same
> spinlock, potentially causing the two to wait for each
> other indefinitely.

That's because the console functions might be called with irqs off and
so in these functions the _irqsave variants are used for locking.

(In general not using spin_lock_irqsave but plain spin_lock in an irq
handler is fine iff there are no other functions taking the lock that
might run with irqs off.)

> Use spin_lock_irqsave() instead to disable interrupts
> upon acquisition of the spinlock.
> 
> Signed-off-by: Sam Nobs <samuel.nobs@xxxxxxxxxxxxx>
> ---
> Here's the lockdep splat for reference:
> 
>    ================================
>    WARNING: inconsistent lock state
>    5.4.46 #1 Not tainted
>    --------------------------------
>    inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage.
>    irq/26-30860000/992 [HC0[0]:SC0[2]:HE1:SE0] takes:
>    ffff00006671d098 (&port_lock_key){?...}, at: imx_uart_int+0x28/0x338
>    {IN-HARDIRQ-W} state was registered at:
>      lock_acquire+0xd0/0x288
>      _raw_spin_lock_irqsave+0x58/0x80
>      imx_uart_console_write+0x1e4/0x220
>      console_unlock+0x2ac/0x610
>      vprintk_emit+0x178/0x330
>      vprintk_default+0x48/0x58
>      vprintk_func+0xe4/0x248
>      printk+0x70/0x90
>      crng_fast_load+0x1c4/0x1c8
>      add_interrupt_randomness+0x348/0x3e8
>      handle_irq_event_percpu+0x50/0x98
>      handle_irq_event+0x4c/0xd0
>      handle_fasteoi_irq+0xe0/0x188
>      generic_handle_irq+0x34/0x50
>      __handle_domain_irq+0x98/0x108
>      gic_handle_irq+0xd4/0x178
>      el1_irq+0xbc/0x180
>      arch_cpu_idle+0x34/0x220
>      default_idle_call+0x40/0x4c
>      do_idle+0x254/0x268
>      cpu_startup_entry+0x28/0x48
>      rest_init+0x1b4/0x284
>      arch_call_rest_init+0x14/0x1c
>      start_kernel+0x48c/0x4bc
>    irq event stamp: 30
>    hardirqs last  enabled at (29): [<ffff800010b22a60>] _raw_spin_unlock_irq+0x38/0x70
>    hardirqs last disabled at (28): [<ffff800010b1b060>] __schedule+0xb0/0x770
>    softirqs last  enabled at (0): [<ffff8000100b28c0>] copy_process+0x8d8/0x19b0
>    softirqs last disabled at (30): [<ffff8000101343f8>] irq_forced_thread_fn+0x0/0xc0
> 
>    other info that might help us debug this:
>     Possible unsafe locking scenario:
> 
>           CPU0
>           ----
>      lock(&port_lock_key);
>      <Interrupt>
>        lock(&port_lock_key);
> 
>     *** DEADLOCK ***
> 
>    no locks held by irq/26-30860000/992.
> 
>    stack backtrace:
>    CPU: 0 PID: 992 Comm: irq/26-30860000 Not tainted 5.4.46 #1
>    Hardware name: Tait i.MX8MM smarc-rcb (DT)
>    Call trace:
>     dump_backtrace+0x0/0x178
>     show_stack+0x24/0x30
>     dump_stack+0xdc/0x144
>     print_usage_bug+0x1c8/0x1e0
>     mark_lock+0x57c/0x740
>     __lock_acquire+0x684/0x16d0
>     lock_acquire+0xd0/0x288
>     _raw_spin_lock+0x44/0x58
>     imx_uart_int+0x28/0x338
>     irq_forced_thread_fn+0x40/0xc0
>     irq_thread+0x1ac/0x280
>     kthread+0x138/0x140
>     ret_from_fork+0x10/0x18
> 
>  drivers/tty/serial/imx.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index 1731d97..29ceaea 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -942,8 +942,9 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id)
>  	struct imx_port *sport = dev_id;
>  	unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4;
>  	irqreturn_t ret = IRQ_NONE;
> +	unsigned long flags = 0;
>  
> -	spin_lock(&sport->port.lock);
> +	spin_lock_irqsave(&sport->port.lock, flags);

There was an earlier commit (c974991d2620419fe21508fc4529014369d16df7)
that changed spin_lock_irqsave to spin_lock under the assumption that in
the irq handler irqs are disabled. I added the author and the reviewer
of this patch to Cc.

To prevent the next person to think this can be converted to spin_lock
I'd like to have a comment here that tells about why we need the irqsave
variant.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux