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

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

 



Hi Sarah,

Have you had a chance to look at Alan's response, and my patch below?

Jack

On Tue, Oct 08, 2013 at 07:02:31PM -0700, Jack Pham wrote:
> 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