53e2c98ce5cdea952ff80d4115bf1ac5d9ff3f73
[oweals/openwrt.git] /
1 From ea7ff2070d564858c445cfdbd883ea00927c0ada Mon Sep 17 00:00:00 2001
2 From: P33M <p33m@github.com>
3 Date: Tue, 9 Apr 2019 16:40:48 +0100
4 Subject: [PATCH] dwc_otg: fix locking around dequeueing and killing
5  URBs
6
7 kill_urbs_in_qh_list() is practically only ever called with the fiq lock
8 already held, so don't spinlock twice in the case where we need to cancel
9 an isochronous transfer.
10
11 Also fix up a case where the global interrupt register could be read with
12 the fiq lock not held.
13
14 Fixes the deadlock seen in https://github.com/raspberrypi/linux/issues/2907
15 ---
16  drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 9 +++++++--
17  drivers/usb/host/dwc_otg/dwc_otg_hcd.c      | 4 ----
18  2 files changed, 7 insertions(+), 6 deletions(-)
19
20 --- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
21 +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
22 @@ -1344,16 +1344,21 @@ static inline uint32_t dwc_otg_read_comm
23                  */
24                 gintmsk_common.b.portintr = 1;
25         }
26 -       gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
27 -       gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
28         if(fiq_enable) {
29                 local_fiq_disable();
30 +               fiq_fsm_spin_lock(&hcd->fiq_state->lock);
31 +               gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
32 +               gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
33                 /* Pull in the interrupts that the FIQ has masked */
34                 gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32);
35                 gintmsk.d32 |= gintmsk_common.d32;
36                 /* for the upstairs function to reenable - have to read it here in case FIQ triggers again */
37                 reenable_gintmsk->d32 = gintmsk.d32;
38 +               fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
39                 local_fiq_enable();
40 +       } else {
41 +               gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
42 +               gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
43         }
44  
45         gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
46 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
47 +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
48 @@ -195,15 +195,11 @@ static void kill_urbs_in_qh_list(dwc_otg
49                          * but not yet been through the IRQ handler.
50                          */
51                         if (fiq_fsm_enable && (hcd->fiq_state->channel[qh->channel->hc_num].fsm != FIQ_PASSTHROUGH)) {
52 -                               local_fiq_disable();
53 -                               fiq_fsm_spin_lock(&hcd->fiq_state->lock);
54                                 qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
55                                 qh->channel->halt_pending = 1;
56                                 if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
57                                         hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
58                                         hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
59 -                               fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
60 -                               local_fiq_enable();
61                         } else {
62                                 dwc_otg_hc_halt(hcd->core_if, qh->channel,
63                                                 DWC_OTG_HC_XFER_URB_DEQUEUE);