| From 496faf29f63753730016a168773c24173f818c45 Mon Sep 17 00:00:00 2001 |
| From: Alan Stern <stern@rowland.harvard.edu> |
| Date: Wed, 17 May 2017 18:32:03 +0300 |
| Subject: [PATCH 221/286] USB: xhci: fix lock-inversion problem |
| |
| With threaded interrupts, bottom-half handlers are called with |
| interrupts enabled. Therefore they can't safely use spin_lock(); they |
| have to use spin_lock_irqsave(). Lockdep warns about a violation |
| occurring in xhci_irq(): |
| |
| ========================================================= |
| [ INFO: possible irq lock inversion dependency detected ] |
| 4.11.0-rc8-dbg+ #1 Not tainted |
| --------------------------------------------------------- |
| swapper/7/0 just changed the state of lock: |
| (&(&ehci->lock)->rlock){-.-...}, at: [<ffffffffa0130a69>] |
| ehci_hrtimer_func+0x29/0xc0 [ehci_hcd] |
| but this lock took another, HARDIRQ-unsafe lock in the past: |
| (hcd_urb_list_lock){+.....} |
| |
| and interrupts could create inverse lock ordering between them. |
| |
| other info that might help us debug this: |
| Possible interrupt unsafe locking scenario: |
| |
| CPU0 CPU1 |
| ---- ---- |
| lock(hcd_urb_list_lock); |
| local_irq_disable(); |
| lock(&(&ehci->lock)->rlock); |
| lock(hcd_urb_list_lock); |
| <Interrupt> |
| lock(&(&ehci->lock)->rlock); |
| *** DEADLOCK *** |
| |
| no locks held by swapper/7/0. |
| the shortest dependencies between 2nd lock and 1st lock: |
| -> (hcd_urb_list_lock){+.....} ops: 252 { |
| HARDIRQ-ON-W at: |
| __lock_acquire+0x602/0x1280 |
| lock_acquire+0xd5/0x1c0 |
| _raw_spin_lock+0x2f/0x40 |
| usb_hcd_unlink_urb_from_ep+0x1b/0x60 [usbcore] |
| xhci_giveback_urb_in_irq.isra.45+0x70/0x1b0 [xhci_hcd] |
| finish_td.constprop.60+0x1d8/0x2e0 [xhci_hcd] |
| xhci_irq+0xdd6/0x1fa0 [xhci_hcd] |
| usb_hcd_irq+0x26/0x40 [usbcore] |
| irq_forced_thread_fn+0x2f/0x70 |
| irq_thread+0x149/0x1d0 |
| kthread+0x113/0x150 |
| ret_from_fork+0x2e/0x40 |
| |
| This patch fixes the problem. |
| |
| Signed-off-by: Alan Stern <stern@rowland.harvard.edu> |
| Reported-and-tested-by: Bart Van Assche <bart.vanassche@sandisk.com> |
| CC: <stable@vger.kernel.org> |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| (cherry picked from commit 63aea0dbab90a2461faaae357cbc8cfd6c8de9fe) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| drivers/usb/host/xhci-ring.c | 5 +++-- |
| 1 file changed, 3 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -2680,11 +2680,12 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd |
| struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
| union xhci_trb *event_ring_deq; |
| irqreturn_t ret = IRQ_NONE; |
| + unsigned long flags; |
| dma_addr_t deq; |
| u64 temp_64; |
| u32 status; |
| |
| - spin_lock(&xhci->lock); |
| + spin_lock_irqsave(&xhci->lock, flags); |
| /* Check if the xHC generated the interrupt, or the irq is shared */ |
| status = readl(&xhci->op_regs->status); |
| if (status == ~(u32)0) { |
| @@ -2757,7 +2758,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd |
| ret = IRQ_HANDLED; |
| |
| out: |
| - spin_unlock(&xhci->lock); |
| + spin_unlock_irqrestore(&xhci->lock, flags); |
| |
| return ret; |
| } |