Re: XHCI: Handling of Zero length packet in data stage

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

 



Hi Sarah, Alan,

I'm taking over reporting of this issue for Hemant while he is out on
vacation.

On Mon, Oct 07, 2013 at 05:44:56PM -0400, Alan Stern wrote:
> On Mon, 7 Oct 2013, Sarah Sharp wrote:
> > What should urb->actual_length be set to instead?  The device sent
> > zero bytes in the data stage and urb->transfer_buffer_length *is*
> > zero, so it
>
> No, urb->transfer_buffer_length > 0.  That's the point.
>
> If I understand the OP correctly, he is saying that a 0-length
> response to (for example) an 8-byte control-in transfer will end up
> with urb->actual_length set to 8 rather than 0.

That's correct.

In our case we are not setting URB_SHORT_NOT_OK, and the URB's
transfer_buffer_length is non-zero, so the data stage TRB does get
enqueued. The device responds with a zero-length data packet but that
is resulting in the URB incorrectly getting its actual_length set to
transfer_buffer_length, instead of 0 as we would expect.

This problem is not seen when connected to an EHCI controller.

On Tue, Oct 08, 2013 at 10:19:11AM -0400, Alan Stern wrote:
> On Mon, 7 Oct 2013, Sarah Sharp wrote:
> 
> > > i am using 3.10 kernel. Also i looked at tip i see same implementation for
> > > process_ctrl_td()
> > > 
> > > https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/host/xhci-ring.c
> > 
> > Yeah, the implementation has always been this way.  I've known since the
> > beginning of writing the xHCI driver that it needs to handle zero-length
> > data phases, but hadn't had ever had a report that it was required by a
> > device under Linux.
> 
> This code, near the end of process_ctrl_td(), looks a little strange:
> 
> 	/*
> 	 * Did we transfer any data, despite the errors that might have
> 	 * happened?  I.e. did we get past the setup stage?
> 	 */
> 	if (event_trb != ep_ring->dequeue) {
> 		/* The event was for the status stage */
> 		if (event_trb == td->last_trb) {
> 			if (td->urb->actual_length != 0) {
> 				/* Don't overwrite a previously set error code
> 				 */
> 				if ((*status == -EINPROGRESS || *status == 0) &&
> 						(td->urb->transfer_flags
> 						 & URB_SHORT_NOT_OK))
> 					/* Did we already see a short data
> 					 * stage? */
> 					*status = -EREMOTEIO;
> 
> If you already saw a short data stage, why isn't the status already set
> to -EREMOTEIO?
> 
> Also what's the reason for the test "td->urb->actual_length != 0"?  
> What does this have to do with getting a short data stage?  Are you 
> assuming that at this point, actual_length will be nonzero if and only 
> if there was a short data stage?
> 
> 			} else {
> 				td->urb->actual_length =
> 					td->urb->transfer_buffer_length;
> 			}
> 
> What's the purpose of this clause?
> 
> It looks like the driver is confusing "actual_length == 0" with 
> "actual_length was never set".  What if actual_length _was_ previously 
> set, but it was set to 0?

Alan, your analysis is consistent with our findings as well. We are
seeing that if actual_length got set to 0 in the data stage, then when
the status stage is processed the above else clause ends up overwriting
this value before handing back the URB. Thus the zero-length packet is
incorrectly reported as a successful transfer of transfer_buffer_length.

Here's a patch we came up with that seems to fix the immediate issue.
However, I'm not sure if this has other side effects or not, e.g. if
URB_SHORT_NOT_OK is set.

Jack

----8<----
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 204110c..507a72d 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2056,6 +2056,8 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
 					/* Did we already see a short data
 					 * stage? */
 					*status = -EREMOTEIO;
+			} else if (td->zlp_data) {
+				td->zlp_data = false;
 			} else {
 				td->urb->actual_length =
 					td->urb->transfer_buffer_length;
@@ -2065,6 +2067,10 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
 			td->urb->actual_length =
 				td->urb->transfer_buffer_length -
 				EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+
+			if (td->urb->actual_length == 0)
+				td->zlp_data = true;
+
 			xhci_dbg(xhci, "Waiting for status "
 					"stage event\n");
 			return 0;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 773583a..25e751a 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1258,6 +1258,9 @@ struct xhci_td {
 	struct xhci_segment	*start_seg;
 	union xhci_trb		*first_trb;
 	union xhci_trb		*last_trb;
+
+	/* ZLP received in data stage of a control transfer */
+	bool			zlp_data;
 };
 
 /* xHCI command default timeout value */
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
--
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