Re: [PATCH 2/3] xhci: Clarify some expressions in the TRB math and fix some errors that this reveals

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

 



Paul, I think I see the bug you're trying to fix.  Can you confirm my
analysis is correct?

This is the original code in xhci-ring.c:

static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb)
{
        int num_sgs, num_trbs, running_total, temp, i;
        struct scatterlist *sg;

        sg = NULL;
        num_sgs = urb->num_sgs;
        temp = urb->transfer_buffer_length;

        xhci_dbg(xhci, "count sg list trbs: \n");
        num_trbs = 0;
        for_each_sg(urb->sg, sg, num_sgs, i) {
                unsigned int previous_total_trbs = num_trbs;
                unsigned int len = sg_dma_len(sg);
        
                /* Scatter gather list entries may cross 64KB boundaries */
                running_total = TRB_MAX_BUFF_SIZE -
                        (sg_dma_address(sg) & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
                if (running_total != 0)
                        num_trbs++;

                /* How many more 64KB chunks to transfer, how many more TRBs? */
                while (running_total < sg_dma_len(sg)) {
                        num_trbs++;
                        running_total += TRB_MAX_BUFF_SIZE;
                }
...
                len = min_t(int, len, temp);
                temp -= len;
                if (temp == 0)
                        break;
        }
...
	return num_trbs;
}

I see that you've changed the while loop condition to this:
> +		while (running_total < sg_dma_len(sg) && running_total < temp) {

I think that's the only bug fix in this patch; all the rest of the
changes look like clean up.

So the original code would fail if the driver submitted a scatter gather
list where the total for all the entries was greater than
urb->transfer_buffer_length, and at least one of the entries in the list
was greater than 64KB.  Is that correct?

I think something like this scatter gather list might fail, if
urb->transfer_buffer_length = 5K.

 -----------------------
|       entry 1         |
|         4K            |
 -----------------------
 -----------------------
|       entry 2         |
|         3K            |
|                       |
|  -- 64KB boundary --  |
|                       |
|         1K            |
 -----------------------

The original code would say there needs to be 3 TRBs for this list, when
there only needs to be two TRBs.  Is that the bug you're trying to fix?

Sarah Sharp

On Thu, Jan 13, 2011 at 12:09:14PM -0800, Paul Zimmerman wrote:
> xhci: Clarify some expressions in the TRB math, and fix some errors (I believe)
> that this reveals. This allows the driver to work with the Synopsys xHCI controller.
> I also verified that it still works with the NEC controller.
> 
> Signed-off-by: Paul Zimmerman <paulz@xxxxxxxxxxxx>
> Cc: Greg Kroah-Hartman <gregkh@xxxxxxx>
> ---
>  drivers/usb/host/xhci-ring.c |   27 ++++++++++++++-------------
>  1 files changed, 14 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index b4be841..16632f1 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c
> @@ -2374,12 +2374,13 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb)
>  
>  		/* Scatter gather list entries may cross 64KB boundaries */
>  		running_total = TRB_MAX_BUFF_SIZE -
> -			(sg_dma_address(sg) & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
> +			(sg_dma_address(sg) & (TRB_MAX_BUFF_SIZE - 1));
> +		running_total &= TRB_MAX_BUFF_SIZE - 1;
>  		if (running_total != 0)
>  			num_trbs++;
>  
>  		/* How many more 64KB chunks to transfer, how many more TRBs? */
> -		while (running_total < sg_dma_len(sg)) {
> +		while (running_total < sg_dma_len(sg) && running_total < temp) {
>  			num_trbs++;
>  			running_total += TRB_MAX_BUFF_SIZE;
>  		}
> @@ -2404,11 +2405,11 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb)
>  static void check_trb_math(struct urb *urb, int num_trbs, int running_total)
>  {
>  	if (num_trbs != 0)
> -		dev_dbg(&urb->dev->dev, "%s - ep %#x - Miscalculated number of "
> +		dev_err(&urb->dev->dev, "%s - ep %#x - Miscalculated number of "
>  				"TRBs, %d left\n", __func__,
>  				urb->ep->desc.bEndpointAddress, num_trbs);
>  	if (running_total != urb->transfer_buffer_length)
> -		dev_dbg(&urb->dev->dev, "%s - ep %#x - Miscalculated tx length, "
> +		dev_err(&urb->dev->dev, "%s - ep %#x - Miscalculated tx length, "
>  				"queued %#x (%d), asked for %#x (%d)\n",
>  				__func__,
>  				urb->ep->desc.bEndpointAddress,
> @@ -2540,8 +2541,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  	sg = urb->sg;
>  	addr = (u64) sg_dma_address(sg);
>  	this_sg_len = sg_dma_len(sg);
> -	trb_buff_len = TRB_MAX_BUFF_SIZE -
> -		(addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
> +	trb_buff_len = TRB_MAX_BUFF_SIZE - (addr & (TRB_MAX_BUFF_SIZE - 1));
>  	trb_buff_len = min_t(int, trb_buff_len, this_sg_len);
>  	if (trb_buff_len > urb->transfer_buffer_length)
>  		trb_buff_len = urb->transfer_buffer_length;
> @@ -2577,7 +2577,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  				(unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1),
>  				(unsigned int) addr + trb_buff_len);
>  		if (TRB_MAX_BUFF_SIZE -
> -				(addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)) < trb_buff_len) {
> +				(addr & (TRB_MAX_BUFF_SIZE - 1)) < trb_buff_len) {
>  			xhci_warn(xhci, "WARN: sg dma xfer crosses 64KB boundaries!\n");
>  			xhci_dbg(xhci, "Next boundary at %#x, end dma = %#x\n",
>  					(unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1),
> @@ -2621,7 +2621,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  		}
>  
>  		trb_buff_len = TRB_MAX_BUFF_SIZE -
> -			(addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
> +			(addr & (TRB_MAX_BUFF_SIZE - 1));
>  		trb_buff_len = min_t(int, trb_buff_len, this_sg_len);
>  		if (running_total + trb_buff_len > urb->transfer_buffer_length)
>  			trb_buff_len =
> @@ -2661,7 +2661,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  	num_trbs = 0;
>  	/* How much data is (potentially) left before the 64KB boundary? */
>  	running_total = TRB_MAX_BUFF_SIZE -
> -		(urb->transfer_dma & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
> +		(urb->transfer_dma & (TRB_MAX_BUFF_SIZE - 1));
> +	running_total &= TRB_MAX_BUFF_SIZE - 1;
>  
>  	/* If there's some data on this 64KB chunk, or we have to send a
>  	 * zero-length transfer, we need at least one TRB
> @@ -2704,8 +2705,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
>  	/* How much data is in the first TRB? */
>  	addr = (u64) urb->transfer_dma;
>  	trb_buff_len = TRB_MAX_BUFF_SIZE -
> -		(urb->transfer_dma & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
> -	if (urb->transfer_buffer_length < trb_buff_len)
> +		(urb->transfer_dma & (TRB_MAX_BUFF_SIZE - 1));
> +	if (trb_buff_len > urb->transfer_buffer_length)
>  		trb_buff_len = urb->transfer_buffer_length;
>  
>  	first_trb = true;
> @@ -2877,8 +2878,8 @@ static int count_isoc_trbs_needed(struct xhci_hcd *xhci,
>  	addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset);
>  	td_len = urb->iso_frame_desc[i].length;
>  
> -	running_total = TRB_MAX_BUFF_SIZE -
> -			(addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
> +	running_total = TRB_MAX_BUFF_SIZE - (addr & (TRB_MAX_BUFF_SIZE - 1));
> +	running_total &= TRB_MAX_BUFF_SIZE - 1;
>  	if (running_total != 0)
>  		num_trbs++;
>  
> -- 
> 1.6.5.1.69.g36942
> 
> 
> 
--
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