> On 29.9.2019 9.07, Peter Chen wrote: > > On some situations, the software handles TRB events slower than adding > > TRBs, then xhci_handle_event can't return zero long time, the xHC will > > consider the event ring is full, and trigger "Event Ring Full" error, > > but in fact, the software has already finished lots of events, just no > > chance to update ERDP (event ring dequeue pointer). > > > > In this commit, we force update ERDP if half of TRBS_PER_SEGMENT > > events have handled to avoid "Event Ring Full" error. > > > > Signed-off-by: Peter Chen <peter.chen@xxxxxxx> > > --- > > Changes for v2: > > - If current ERDP value is the same with intended written one, > > give up this written. > > > > drivers/usb/host/xhci-ring.c | 62 ++++++++++++++++++++++++++---------- > > 1 file changed, 45 insertions(+), 17 deletions(-) > > > > diff --git a/drivers/usb/host/xhci-ring.c > > b/drivers/usb/host/xhci-ring.c index e220bcbee173..2c0a15c3b3a7 100644 > > --- a/drivers/usb/host/xhci-ring.c > > +++ b/drivers/usb/host/xhci-ring.c > > @@ -2737,6 +2737,44 @@ static int xhci_handle_event(struct xhci_hcd *xhci) > > return 1; > > } > > > > +/* > > + * Update Event Ring Dequeue Pointer: > > + * - When all events have finished > > + * - To avoid "Event Ring Full Error" condition */ static void > > +xhci_update_erst_dequeue(struct xhci_hcd *xhci, > > + union xhci_trb *event_ring_deq) > > +{ > > + u64 temp_64; > > + dma_addr_t deq; > > + > > + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); > > + /* If necessary, update the HW's version of the event ring deq ptr. */ > > + if (event_ring_deq != xhci->event_ring->dequeue) { > > + deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, > > + xhci->event_ring->dequeue); > > + if (deq == 0) > > + xhci_warn(xhci, "WARN something wrong with SW event " > > + "ring dequeue ptr.\n"); > > + > > + /* > > + * Per 4.9.4, Software writes to the ERDP register shall > > + * always advance the Event Ring Dequeue Pointer value. > > + */ > > + if ((temp_64 & (u64) ~ERST_PTR_MASK) == > > + ((u64) deq & (u64) ~ERST_PTR_MASK)) > > + return; > > + > > + /* Update HC event ring dequeue pointer */ > > + temp_64 &= ERST_PTR_MASK; > > + temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); > > + } > > + > > + /* Clear the event handler busy flag (RW1C) */ > > + temp_64 |= ERST_EHB; > > + xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); } > > + > > /* > > * xHCI spec says we can get an interrupt, and if the HC has an error condition, > > * we might get bad data out of the event ring. Section 4.10.2.7 > > has a list of @@ -2748,9 +2786,9 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) > > union xhci_trb *event_ring_deq; > > irqreturn_t ret = IRQ_NONE; > > unsigned long flags; > > - dma_addr_t deq; > > u64 temp_64; > > u32 status; > > + int event_loop = 0; > > > > spin_lock_irqsave(&xhci->lock, flags); > > /* Check if the xHC generated the interrupt, or the irq is shared > > */ @@ -2804,24 +2842,14 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) > > /* FIXME this should be a delayed service routine > > * that clears the EHB. > > */ > > - while (xhci_handle_event(xhci) > 0) {} > > - > > - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); > > - /* If necessary, update the HW's version of the event ring deq ptr. */ > > - if (event_ring_deq != xhci->event_ring->dequeue) { > > - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, > > - xhci->event_ring->dequeue); > > - if (deq == 0) > > - xhci_warn(xhci, "WARN something wrong with SW event " > > - "ring dequeue ptr.\n"); > > - /* Update HC event ring dequeue pointer */ > > - temp_64 &= ERST_PTR_MASK; > > - temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); > > + while (xhci_handle_event(xhci) > 0) { > > + if (event_loop++ < TRBS_PER_SEGMENT / 2) > > + continue; > > + xhci_update_erst_dequeue(xhci, event_ring_deq); > > + event_loop = 0; > > } > > > > - /* Clear the event handler busy flag (RW1C); event ring is empty. */ > > - temp_64 |= ERST_EHB; > > - xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); > > + xhci_update_erst_dequeue(xhci, event_ring_deq); > > ret = IRQ_HANDLED; > > > > out: > > > > This should work, adding to queue Thanks, I have tested these days, no issue has found so far. Peter