aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-10-27 13:55:52 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-11 14:55:17 -0500
commit678539cfaa090093a9aef185f545f6b17acc445c (patch)
tree44d5be527d8e9a5bc8facbc1fdd95cf192765996 /drivers/usb
parent0c487206fe925ef370e1fc092003efb74ad57410 (diff)
USB: xhci: Handle URB cancel, complete and resubmit race.
In the old code, there was a race condition between the stop endpoint command and the URB submission process. When the stop endpoint command is handled by the event handler, the endpoint ring is assumed to be stopped. When a stop endpoint command is queued, URB submissions are to not ring the doorbell. The old code would check the number of pending URBs to be canceled, and would not ring the doorbell if it was non-zero. However, the following race condition could occur with the old code: 1. Cancel an URB, add it to the list of URBs to be canceled, queue the stop endpoint command, and increment ep->cancels_pending to 1. 2. The URB finishes on the HW, and an event is enqueued to the event ring (at the same time as 1). 3. The stop endpoint command finishes, and the endpoint is halted. An event is queued to the event ring. 4. The event handler sees the finished URB, notices it was to be canceled, decrements ep->cancels_pending to 0, and removes it from the to be canceled list. 5. The event handler drops the lock and gives back the URB. The completion handler requeues the URB (or a different driver enqueues a new URB). This causes the endpoint's doorbell to be rung, since ep->cancels_pending == 0. The endpoint is now running. 6. A second URB is canceled, and it's added to the canceled list. Since ep->cancels_pending == 0, a new stop endpoint command is queued, and ep->cancels_pending is incremented to 1. 7. The event handler then sees the completed stop endpoint command. The handler assumes the endpoint is stopped, but it isn't. It attempts to move the dequeue pointer or change TDs to cancel the second URB, while the hardware is actively accessing the endpoint ring. To eliminate this race condition, a new endpoint state bit is introduced, EP_HALT_PENDING. When this bit is set, a stop endpoint command has been queued, and the command handler has not begun to process the URB cancellation list yet. The endpoint doorbell should not be rung when this is set. Set this when a stop endpoint command is queued, clear it when the handler for that command runs, and check if it's set before ringing a doorbell. ep->cancels_pending is eliminated, because it is no longer used. Make sure to ring the doorbell for an endpoint when the stop endpoint command handler runs, even if the canceled URB list is empty. All canceled URBs could have completed and new URBs could have been enqueued without the doorbell being rung before the command was handled. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/xhci-hcd.c4
-rw-r--r--drivers/usb/host/xhci-ring.c14
-rw-r--r--drivers/usb/host/xhci.h4
3 files changed, 12 insertions, 10 deletions
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c
index 932f9993848..3a30db6d6ab 100644
--- a/drivers/usb/host/xhci-hcd.c
+++ b/drivers/usb/host/xhci-hcd.c
@@ -817,12 +817,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
817 xhci_debug_ring(xhci, ep_ring); 817 xhci_debug_ring(xhci, ep_ring);
818 td = (struct xhci_td *) urb->hcpriv; 818 td = (struct xhci_td *) urb->hcpriv;
819 819
820 ep->cancels_pending++;
821 list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list); 820 list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
822 /* Queue a stop endpoint command, but only if this is 821 /* Queue a stop endpoint command, but only if this is
823 * the first cancellation to be handled. 822 * the first cancellation to be handled.
824 */ 823 */
825 if (ep->cancels_pending == 1) { 824 if (!(ep->ep_state & EP_HALT_PENDING)) {
825 ep->ep_state |= EP_HALT_PENDING;
826 xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index); 826 xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index);
827 xhci_ring_cmd_db(xhci); 827 xhci_ring_cmd_db(xhci);
828 } 828 }
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 821b7b4709d..184e8b6f30b 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -306,7 +306,7 @@ static void ring_ep_doorbell(struct xhci_hcd *xhci,
306 /* Don't ring the doorbell for this endpoint if there are pending 306 /* Don't ring the doorbell for this endpoint if there are pending
307 * cancellations because the we don't want to interrupt processing. 307 * cancellations because the we don't want to interrupt processing.
308 */ 308 */
309 if (!ep->cancels_pending && !(ep_state & SET_DEQ_PENDING) 309 if (!(ep_state & EP_HALT_PENDING) && !(ep_state & SET_DEQ_PENDING)
310 && !(ep_state & EP_HALTED)) { 310 && !(ep_state & EP_HALTED)) {
311 field = xhci_readl(xhci, db_addr) & DB_MASK; 311 field = xhci_readl(xhci, db_addr) & DB_MASK;
312 xhci_writel(xhci, field | EPI_TO_DB(ep_index), db_addr); 312 xhci_writel(xhci, field | EPI_TO_DB(ep_index), db_addr);
@@ -507,8 +507,11 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
507 ep = &xhci->devs[slot_id]->eps[ep_index]; 507 ep = &xhci->devs[slot_id]->eps[ep_index];
508 ep_ring = ep->ring; 508 ep_ring = ep->ring;
509 509
510 if (list_empty(&ep->cancelled_td_list)) 510 if (list_empty(&ep->cancelled_td_list)) {
511 ep->ep_state &= ~EP_HALT_PENDING;
512 ring_ep_doorbell(xhci, slot_id, ep_index);
511 return; 513 return;
514 }
512 515
513 /* Fix up the ep ring first, so HW stops executing cancelled TDs. 516 /* Fix up the ep ring first, so HW stops executing cancelled TDs.
514 * We have the xHCI lock, so nothing can modify this list until we drop 517 * We have the xHCI lock, so nothing can modify this list until we drop
@@ -535,9 +538,9 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
535 * the cancelled TD list for URB completion later. 538 * the cancelled TD list for URB completion later.
536 */ 539 */
537 list_del(&cur_td->td_list); 540 list_del(&cur_td->td_list);
538 ep->cancels_pending--;
539 } 541 }
540 last_unlinked_td = cur_td; 542 last_unlinked_td = cur_td;
543 ep->ep_state &= ~EP_HALT_PENDING;
541 544
542 /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */ 545 /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */
543 if (deq_state.new_deq_ptr && deq_state.new_deq_seg) { 546 if (deq_state.new_deq_ptr && deq_state.new_deq_seg) {
@@ -1249,10 +1252,9 @@ td_cleanup:
1249 } 1252 }
1250 list_del(&td->td_list); 1253 list_del(&td->td_list);
1251 /* Was this TD slated to be cancelled but completed anyway? */ 1254 /* Was this TD slated to be cancelled but completed anyway? */
1252 if (!list_empty(&td->cancelled_td_list)) { 1255 if (!list_empty(&td->cancelled_td_list))
1253 list_del(&td->cancelled_td_list); 1256 list_del(&td->cancelled_td_list);
1254 ep->cancels_pending--; 1257
1255 }
1256 /* Leave the TD around for the reset endpoint function to use 1258 /* Leave the TD around for the reset endpoint function to use
1257 * (but only if it's not a control endpoint, since we already 1259 * (but only if it's not a control endpoint, since we already
1258 * queued the Set TR dequeue pointer command for stalled 1260 * queued the Set TR dequeue pointer command for stalled
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 4b254b6fa24..b173fd96dce 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -652,10 +652,10 @@ struct xhci_virt_ep {
652 struct xhci_ring *new_ring; 652 struct xhci_ring *new_ring;
653 unsigned int ep_state; 653 unsigned int ep_state;
654#define SET_DEQ_PENDING (1 << 0) 654#define SET_DEQ_PENDING (1 << 0)
655#define EP_HALTED (1 << 1) 655#define EP_HALTED (1 << 1) /* For stall handling */
656#define EP_HALT_PENDING (1 << 2) /* For URB cancellation */
656 /* ---- Related to URB cancellation ---- */ 657 /* ---- Related to URB cancellation ---- */
657 struct list_head cancelled_td_list; 658 struct list_head cancelled_td_list;
658 unsigned int cancels_pending;
659 /* The TRB that was last reported in a stopped endpoint ring */ 659 /* The TRB that was last reported in a stopped endpoint ring */
660 union xhci_trb *stopped_trb; 660 union xhci_trb *stopped_trb;
661 struct xhci_td *stopped_td; 661 struct xhci_td *stopped_td;