aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2011-09-19 19:05:12 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-09-19 20:15:47 -0400
commitc2d7b49f42f50d7fc5cbfd195b785a128723fdf4 (patch)
tree24bedd5bfe4a08fe49757c938b6c59aab9acfa7c
parent44f4c3ed60fb21e1d2dd98304390ac121e6c7c6d (diff)
USB: xHCI: prevent infinite loop when processing MSE event
When a xHC host is unable to handle isochronous transfer in the interval, it reports a Missed Service Error event and skips some tds. Currently xhci driver handles MSE event in the following ways: 1. When encounter a MSE event, set ep->skip flag, update event ring dequeue pointer and return. 2. When encounter the next event on this ep, the driver will run the do-while loop, fetch td from ep's td_list to find the td corresponding to this event. All tds missed are marked as short transfer(-EXDEV). The do-while loop will end in two ways: 1. If the td pointed by the event trb is found; 2. If the ep ring's td_list is empty. However, if a buggy HW reports some unpredicted event (for example, an overrun event following a MSE event while the ep ring is actually not empty), the driver will never find the td, and it will loop until the td_list is empty. Unfortunately, the spinlock is dropped when give back a urb in the do-while loop. During the spinlock released period, the class driver may still submit urbs and add tds to the td_list. This may cause disaster, since the td_list will never be empty and the loop never ends, and the system hangs. To fix this, count the number of TDs on the ep ring before skipping TDs, and quit the loop when skipped that number of tds. This guarantees the do-while loop will end after certain number of cycles, and driver will not be trapped in an infinite loop. Signed-off-by: Andiry Xu <andiry.xu@amd.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/usb/host/xhci-ring.c19
1 files changed, 19 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 54139a2f06ce..952e2ded61af 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1934,8 +1934,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
1934 int status = -EINPROGRESS; 1934 int status = -EINPROGRESS;
1935 struct urb_priv *urb_priv; 1935 struct urb_priv *urb_priv;
1936 struct xhci_ep_ctx *ep_ctx; 1936 struct xhci_ep_ctx *ep_ctx;
1937 struct list_head *tmp;
1937 u32 trb_comp_code; 1938 u32 trb_comp_code;
1938 int ret = 0; 1939 int ret = 0;
1940 int td_num = 0;
1939 1941
1940 slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); 1942 slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
1941 xdev = xhci->devs[slot_id]; 1943 xdev = xhci->devs[slot_id];
@@ -1957,6 +1959,12 @@ static int handle_tx_event(struct xhci_hcd *xhci,
1957 return -ENODEV; 1959 return -ENODEV;
1958 } 1960 }
1959 1961
1962 /* Count current td numbers if ep->skip is set */
1963 if (ep->skip) {
1964 list_for_each(tmp, &ep_ring->td_list)
1965 td_num++;
1966 }
1967
1960 event_dma = le64_to_cpu(event->buffer); 1968 event_dma = le64_to_cpu(event->buffer);
1961 trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); 1969 trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
1962 /* Look for common error cases */ 1970 /* Look for common error cases */
@@ -2068,7 +2076,18 @@ static int handle_tx_event(struct xhci_hcd *xhci,
2068 goto cleanup; 2076 goto cleanup;
2069 } 2077 }
2070 2078
2079 /* We've skipped all the TDs on the ep ring when ep->skip set */
2080 if (ep->skip && td_num == 0) {
2081 ep->skip = false;
2082 xhci_dbg(xhci, "All tds on the ep_ring skipped. "
2083 "Clear skip flag.\n");
2084 ret = 0;
2085 goto cleanup;
2086 }
2087
2071 td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); 2088 td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
2089 if (ep->skip)
2090 td_num--;
2072 2091
2073 /* Is this a TRB in the currently executing TD? */ 2092 /* Is this a TRB in the currently executing TD? */
2074 event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, 2093 event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,