dwc_otg: fix locking around dequeueing and killing URBs
authorP33M <p33m@github.com>
Tue, 9 Apr 2019 15:40:48 +0000 (16:40 +0100)
committerpopcornmix <popcornmix@gmail.com>
Mon, 13 May 2019 23:08:27 +0000 (00:08 +0100)
kill_urbs_in_qh_list() is practically only ever called with the fiq lock
already held, so don't spinlock twice in the case where we need to cancel
an isochronous transfer.

Also fix up a case where the global interrupt register could be read with
the fiq lock not held.

Fixes the deadlock seen in https://github.com/raspberrypi/linux/issues/2907

drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
drivers/usb/host/dwc_otg/dwc_otg_hcd.c

index 9fb7229..799ab14 100644 (file)
@@ -1344,16 +1344,21 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin
                 */
                gintmsk_common.b.portintr = 1;
        }
-       gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
-       gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
        if(fiq_enable) {
                local_fiq_disable();
+               fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+               gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+               gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
                /* Pull in the interrupts that the FIQ has masked */
                gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32);
                gintmsk.d32 |= gintmsk_common.d32;
                /* for the upstairs function to reenable - have to read it here in case FIQ triggers again */
                reenable_gintmsk->d32 = gintmsk.d32;
+               fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
                local_fiq_enable();
+       } else {
+               gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+               gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
        }
 
        gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
index 855afd2..cdfb9a6 100644 (file)
@@ -195,15 +195,11 @@ static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
                         * but not yet been through the IRQ handler.
                         */
                        if (fiq_fsm_enable && (hcd->fiq_state->channel[qh->channel->hc_num].fsm != FIQ_PASSTHROUGH)) {
-                               local_fiq_disable();
-                               fiq_fsm_spin_lock(&hcd->fiq_state->lock);
                                qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
                                qh->channel->halt_pending = 1;
                                if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
                                        hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
                                        hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
-                               fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
-                               local_fiq_enable();
                        } else {
                                dwc_otg_hc_halt(hcd->core_if, qh->channel,
                                                DWC_OTG_HC_XFER_URB_DEQUEUE);