Re: Potential issue in xhci-ring handle_tx_event()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Feb 24, 2011 at 02:07:27PM +0800, Xu, Andiry wrote:
> > -----Original Message-----
> > From: Dmitry Torokhov [mailto:dtor@xxxxxxxxxx]
> > Sent: Thursday, February 24, 2011 12:48 PM
> > To: Xu, Andiry; Sarah Sharp
> > Cc: linux-usb@xxxxxxxxxxxxxxx
> > Subject: Potential issue in xhci-ring handle_tx_event()
> > 
> > Hi Andiry, Sarah,
> > 
> > I was compiling xhci and got a warning that event_trb might be used
> > uninitialized in handle_tx_event(). Looking at the implementation the
> > logic is quite convoluted but appears that if the following condition
> is
> > true:
> > 
> > 	!event_seg && ep->skip && usb_endpoint_xfer_isoc(...)
> > 
> > we indeed will end up calling process_*_td(..., event_trb, ...).
> > 
> 
> Well, if the condition is met, it must be an isoc endpoint and
> process_isoc_td() will be called with ep->skip set. You can follow the
> call trace and see that event_trb will never be used in that case.
> That's a normal case for isoc transfer. Sometimes the xHC is unable to
> process all the isoc tds in time and it will miss some tds. The driver
> will never find the corresponding event_trb for these tds in this case.

Ah, I completely missed the fact that event_trb is not always used by
process_isoc_td().

In this case do you think the following patch clears up logic a bit:

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 3e8211c..8336de6 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1565,71 +1565,66 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
 	union xhci_trb *event_trb, struct xhci_transfer_event *event,
 	struct xhci_virt_ep *ep, int *status)
 {
-	struct xhci_ring *ep_ring;
-	struct urb_priv *urb_priv;
-	int idx;
+	struct xhci_ring *ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+	struct urb_priv *urb_priv = td->urb->hcpriv;
+	int idx = urb_priv->td_cnt;
 	int len = 0;
-	int skip_td = 0;
 	union xhci_trb *cur_trb;
 	struct xhci_segment *cur_seg;
-	u32 trb_comp_code;
-
-	ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
-	trb_comp_code = GET_COMP_CODE(event->transfer_len);
-	urb_priv = td->urb->hcpriv;
-	idx = urb_priv->td_cnt;
+	u32 trb_comp_code = GET_COMP_CODE(event->transfer_len);
+	bool skip_td = false;
 
 	if (ep->skip) {
 		/* The transfer is partly done */
 		*status = -EXDEV;
 		td->urb->iso_frame_desc[idx].status = -EXDEV;
-	} else {
-		/* handle completion code */
-		switch (trb_comp_code) {
-		case COMP_SUCCESS:
-			td->urb->iso_frame_desc[idx].status = 0;
-			xhci_dbg(xhci, "Successful isoc transfer!\n");
-			break;
-		case COMP_SHORT_TX:
-			if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
-				td->urb->iso_frame_desc[idx].status =
-					 -EREMOTEIO;
-			else
-				td->urb->iso_frame_desc[idx].status = 0;
-			break;
-		case COMP_BW_OVER:
-			td->urb->iso_frame_desc[idx].status = -ECOMM;
-			skip_td = 1;
-			break;
-		case COMP_BUFF_OVER:
-		case COMP_BABBLE:
-			td->urb->iso_frame_desc[idx].status = -EOVERFLOW;
-			skip_td = 1;
-			break;
-		case COMP_STALL:
-			td->urb->iso_frame_desc[idx].status = -EPROTO;
-			skip_td = 1;
-			break;
-		case COMP_STOP:
-		case COMP_STOP_INVAL:
-			break;
-		default:
-			td->urb->iso_frame_desc[idx].status = -1;
-			break;
-		}
-	}
 
-	/* calc actual length */
-	if (ep->skip) {
+		/* calc actual length */
 		td->urb->iso_frame_desc[idx].actual_length = 0;
+
 		/* Update ring dequeue pointer */
 		while (ep_ring->dequeue != td->last_trb)
 			inc_deq(xhci, ep_ring, false);
 		inc_deq(xhci, ep_ring, false);
+
 		return finish_td(xhci, td, event_trb, event, ep, status, true);
 	}
 
-	if (trb_comp_code == COMP_SUCCESS || skip_td == 1) {
+	/* handle completion code */
+	switch (trb_comp_code) {
+	case COMP_SUCCESS:
+		td->urb->iso_frame_desc[idx].status = 0;
+		xhci_dbg(xhci, "Successful isoc transfer!\n");
+		break;
+	case COMP_SHORT_TX:
+		if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+			td->urb->iso_frame_desc[idx].status =
+				 -EREMOTEIO;
+		else
+			td->urb->iso_frame_desc[idx].status = 0;
+		break;
+	case COMP_BW_OVER:
+		td->urb->iso_frame_desc[idx].status = -ECOMM;
+		skip_td = true;
+		break;
+	case COMP_BUFF_OVER:
+	case COMP_BABBLE:
+		td->urb->iso_frame_desc[idx].status = -EOVERFLOW;
+		skip_td = true;
+		break;
+	case COMP_STALL:
+		td->urb->iso_frame_desc[idx].status = -EPROTO;
+		skip_td = true;
+		break;
+	case COMP_STOP:
+	case COMP_STOP_INVAL:
+		break;
+	default:
+		td->urb->iso_frame_desc[idx].status = -1;
+		break;
+	}
+
+	if (trb_comp_code == COMP_SUCCESS || skip_td) {
 		td->urb->iso_frame_desc[idx].actual_length =
 			td->urb->iso_frame_desc[idx].length;
 		td->urb->actual_length +=
@@ -1667,13 +1662,10 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
 	union xhci_trb *event_trb, struct xhci_transfer_event *event,
 	struct xhci_virt_ep *ep, int *status)
 {
-	struct xhci_ring *ep_ring;
+	struct xhci_ring *ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
 	union xhci_trb *cur_trb;
 	struct xhci_segment *cur_seg;
-	u32 trb_comp_code;
-
-	ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
-	trb_comp_code = GET_COMP_CODE(event->transfer_len);
+	u32 trb_comp_code = GET_COMP_CODE(event->transfer_len);
 
 	switch (trb_comp_code) {
 	case COMP_SUCCESS:
@@ -1918,22 +1910,27 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 		}
 
 		td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
+
 		/* Is this a TRB in the currently executing TD? */
 		event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
 				td->last_trb, event_dma);
-		if (event_seg && ep->skip) {
-			xhci_dbg(xhci, "Found td. Clear skip flag.\n");
-			ep->skip = false;
-		}
-		if (!event_seg &&
-		   (!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) {
-			/* HC is busted, give up! */
-			xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not "
+		if (!event_seg) {
+			if (!ep->skip ||
+			    !usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
+				/* HC is busted, give up! */
+				xhci_err(xhci,
+					"ERROR Transfer event TRB DMA ptr not "
 					"part of current TD\n");
-			return -ESHUTDOWN;
-		}
+				return -ESHUTDOWN;
+			}
+			/* When skipping event_trb is not used */
+			event_trb = NULL;
+		} else {
+			if (ep->skip) {
+				xhci_dbg(xhci, "Found td. Clear skip flag.\n");
+				ep->skip = false;
+			}
 
-		if (event_seg) {
 			event_trb = &event_seg->trbs[(event_dma -
 					 event_seg->dma) / sizeof(*event_trb)];
 			/*

Thanks.

-- 
Dmitry

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux