Skip to content

Commit d18240d

Browse files
Andiry Xugregkh
authored andcommitted
USB: xHCI: Missed Service Error Event process
This patch adds mechanism to process Missed Service Error Event. Sometimes the xHC is unable to process the isoc TDs in time, it will generate Missed Service Error Event. In this case some TDs on the ring are not processed and missed. When encounter a Missed Servce Error Event, set the skip flag of the ep, and process the missed TDs until reach the next processed TD, then clear the skip flag. Signed-off-by: Andiry Xu <[email protected]> Signed-off-by: Sarah Sharp <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 986a92d commit d18240d

File tree

3 files changed

+117
-50
lines changed

3 files changed

+117
-50
lines changed

drivers/usb/host/xhci-mem.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
11241124
virt_dev->num_rings_cached--;
11251125
xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring);
11261126
}
1127+
virt_dev->eps[ep_index].skip = false;
11271128
ep_ring = virt_dev->eps[ep_index].new_ring;
11281129
ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state;
11291130

drivers/usb/host/xhci-ring.c

Lines changed: 108 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,16 @@ static int handle_tx_event(struct xhci_hcd *xhci,
16751675
"still with TDs queued?\n",
16761676
TRB_TO_SLOT_ID(event->flags), ep_index);
16771677
goto cleanup;
1678+
case COMP_MISSED_INT:
1679+
/*
1680+
* When encounter missed service error, one or more isoc tds
1681+
* may be missed by xHC.
1682+
* Set skip flag of the ep_ring; Complete the missed tds as
1683+
* short transfer when process the ep_ring next time.
1684+
*/
1685+
ep->skip = true;
1686+
xhci_dbg(xhci, "Miss service interval error, set skip flag\n");
1687+
goto cleanup;
16781688
default:
16791689
if (xhci_is_vendor_info_code(xhci, trb_comp_code)) {
16801690
status = 0;
@@ -1685,60 +1695,108 @@ static int handle_tx_event(struct xhci_hcd *xhci,
16851695
goto cleanup;
16861696
}
16871697

1688-
/* This TRB should be in the TD at the head of this ring's TD list */
1689-
if (list_empty(&ep_ring->td_list)) {
1690-
xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n",
1691-
TRB_TO_SLOT_ID(event->flags), ep_index);
1692-
xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
1693-
(unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
1694-
xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
1695-
goto cleanup;
1696-
}
1697-
td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
1698-
1699-
/* Is this a TRB in the currently executing TD? */
1700-
event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
1701-
td->last_trb, event_dma);
1702-
if (!event_seg) {
1703-
/* HC is busted, give up! */
1704-
xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n");
1705-
return -ESHUTDOWN;
1706-
}
1707-
event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)];
1698+
do {
1699+
/* This TRB should be in the TD at the head of this ring's
1700+
* TD list.
1701+
*/
1702+
if (list_empty(&ep_ring->td_list)) {
1703+
xhci_warn(xhci, "WARN Event TRB for slot %d ep %d "
1704+
"with no TDs queued?\n",
1705+
TRB_TO_SLOT_ID(event->flags), ep_index);
1706+
xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
1707+
(unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
1708+
xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
1709+
if (ep->skip) {
1710+
ep->skip = false;
1711+
xhci_dbg(xhci, "td_list is empty while skip "
1712+
"flag set. Clear skip flag.\n");
1713+
}
1714+
ret = 0;
1715+
goto cleanup;
1716+
}
17081717

1709-
/* Now update the urb's actual_length and give back to the core */
1710-
/* Was this a control transfer? */
1711-
if (usb_endpoint_xfer_control(&td->urb->ep->desc))
1712-
ret = process_ctrl_td(xhci, td, event_trb, event, ep,
1713-
&status);
1714-
else
1715-
ret = process_bulk_intr_td(xhci, td, event_trb, event, ep,
1716-
&status);
1718+
td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
1719+
/* Is this a TRB in the currently executing TD? */
1720+
event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
1721+
td->last_trb, event_dma);
1722+
if (event_seg && ep->skip) {
1723+
xhci_dbg(xhci, "Found td. Clear skip flag.\n");
1724+
ep->skip = false;
1725+
}
1726+
if (!event_seg &&
1727+
(!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) {
1728+
/* HC is busted, give up! */
1729+
xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not "
1730+
"part of current TD\n");
1731+
return -ESHUTDOWN;
1732+
}
17171733

1718-
cleanup:
1719-
inc_deq(xhci, xhci->event_ring, true);
1720-
xhci_set_hc_event_deq(xhci);
1734+
if (event_seg) {
1735+
event_trb = &event_seg->trbs[(event_dma -
1736+
event_seg->dma) / sizeof(*event_trb)];
1737+
/*
1738+
* No-op TRB should not trigger interrupts.
1739+
* If event_trb is a no-op TRB, it means the
1740+
* corresponding TD has been cancelled. Just ignore
1741+
* the TD.
1742+
*/
1743+
if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
1744+
== TRB_TYPE(TRB_TR_NOOP)) {
1745+
xhci_dbg(xhci, "event_trb is a no-op TRB. "
1746+
"Skip it\n");
1747+
goto cleanup;
1748+
}
1749+
}
17211750

1722-
/* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */
1723-
if (ret) {
1724-
urb = td->urb;
1725-
/* Leave the TD around for the reset endpoint function to use
1726-
* (but only if it's not a control endpoint, since we already
1727-
* queued the Set TR dequeue pointer command for stalled
1728-
* control endpoints).
1751+
/* Now update the urb's actual_length and give back to
1752+
* the core
17291753
*/
1730-
if (usb_endpoint_xfer_control(&urb->ep->desc) ||
1731-
(trb_comp_code != COMP_STALL &&
1732-
trb_comp_code != COMP_BABBLE))
1733-
kfree(td);
1734-
1735-
usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
1736-
xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n",
1737-
urb, urb->actual_length, status);
1738-
spin_unlock(&xhci->lock);
1739-
usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status);
1740-
spin_lock(&xhci->lock);
1741-
}
1754+
if (usb_endpoint_xfer_control(&td->urb->ep->desc))
1755+
ret = process_ctrl_td(xhci, td, event_trb, event, ep,
1756+
&status);
1757+
else
1758+
ret = process_bulk_intr_td(xhci, td, event_trb, event,
1759+
ep, &status);
1760+
1761+
cleanup:
1762+
/*
1763+
* Do not update event ring dequeue pointer if ep->skip is set.
1764+
* Will roll back to continue process missed tds.
1765+
*/
1766+
if (trb_comp_code == COMP_MISSED_INT || !ep->skip) {
1767+
inc_deq(xhci, xhci->event_ring, true);
1768+
xhci_set_hc_event_deq(xhci);
1769+
}
1770+
1771+
if (ret) {
1772+
urb = td->urb;
1773+
/* Leave the TD around for the reset endpoint function
1774+
* to use(but only if it's not a control endpoint,
1775+
* since we already queued the Set TR dequeue pointer
1776+
* command for stalled control endpoints).
1777+
*/
1778+
if (usb_endpoint_xfer_control(&urb->ep->desc) ||
1779+
(trb_comp_code != COMP_STALL &&
1780+
trb_comp_code != COMP_BABBLE))
1781+
kfree(td);
1782+
1783+
usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
1784+
xhci_dbg(xhci, "Giveback URB %p, len = %d, "
1785+
"status = %d\n",
1786+
urb, urb->actual_length, status);
1787+
spin_unlock(&xhci->lock);
1788+
usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status);
1789+
spin_lock(&xhci->lock);
1790+
}
1791+
1792+
/*
1793+
* If ep->skip is set, it means there are missed tds on the
1794+
* endpoint ring need to take care of.
1795+
* Process them as short transfer until reach the td pointed by
1796+
* the event.
1797+
*/
1798+
} while (ep->skip && trb_comp_code != COMP_MISSED_INT);
1799+
17421800
return 0;
17431801
}
17441802

drivers/usb/host/xhci.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,14 @@ struct xhci_virt_ep {
720720
struct timer_list stop_cmd_timer;
721721
int stop_cmds_pending;
722722
struct xhci_hcd *xhci;
723+
/*
724+
* Sometimes the xHC can not process isochronous endpoint ring quickly
725+
* enough, and it will miss some isoc tds on the ring and generate
726+
* a Missed Service Error Event.
727+
* Set skip flag when receive a Missed Service Error Event and
728+
* process the missed tds on the endpoint ring.
729+
*/
730+
bool skip;
723731
};
724732

725733
struct xhci_virt_device {

0 commit comments

Comments
 (0)