Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: dwc3: gadget: Prevent losing events in event cache

The dwc3 driver can overwite its previous events if its top-half IRQ
handler (TH) gets invoked again before processing the events in the
cache. We see this as a hang in the file transfer and the host will
attempt to reset the device. TH gets the event count and deasserts the
interrupt line by writing DWC3_GEVNTSIZ_INTMASK to DWC3_GEVNTSIZ. If
there's a new event coming between reading the event count and interrupt
deassertion, dwc3 will lose previous pending events. More generally, we
will see 0 event count, which should not affect anything.

This shouldn't be possible in the current dwc3 implementation. However,
through testing and reading the PCIe trace, the TH occasionally still
gets invoked one more time after HW interrupt deassertion. (With PCIe
legacy interrupts, TH is called repeatedly as long as the interrupt line
is asserted). We suspect that there is a small detection delay in the
SW.

To avoid this issue, Check DWC3_EVENT_PENDING flag to determine if the
events are processed in the bottom-half IRQ handler. If not, return
IRQ_HANDLED and don't process new event.

Cc: stable@vger.kernel.org
Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>

authored by

Thinh Nguyen and committed by
Felipe Balbi
d325a1de f1d6826c

+9
+9
drivers/usb/dwc3/gadget.c
··· 3036 3036 return IRQ_HANDLED; 3037 3037 } 3038 3038 3039 + /* 3040 + * With PCIe legacy interrupt, test shows that top-half irq handler can 3041 + * be called again after HW interrupt deassertion. Check if bottom-half 3042 + * irq event handler completes before caching new event to prevent 3043 + * losing events. 3044 + */ 3045 + if (evt->flags & DWC3_EVENT_PENDING) 3046 + return IRQ_HANDLED; 3047 + 3039 3048 count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); 3040 3049 count &= DWC3_GEVNTCOUNT_MASK; 3041 3050 if (!count)