diff mbox

[U-Boot,v2] usb_storage: fix ehci driver max transfer size

Message ID fce6d97c0746ecbaaf63f29c112bbc5689c5750f.1341863102.git.stefan@herbrechtsmeier.net
State Accepted
Commit 1b4bd0e66cd3b5124669c78bc968510b1040e9d9
Delegated to: Marek Vasut
Headers show

Commit Message

Stefan Herbrechtsmeier July 9, 2012, 7:52 p.m. UTC
The commit 5dd95cf93dfffa1d19a1928990852aac9f55b9d9 'usb_storage:
Fix EHCI "out of buffer pointers" with CD-ROM' introduce a bug in
usb_storage as it wrongly assumes that every transfer can use
4096 bytes per qt_buffer. This is wrong if the start address of
the data is not page aligned to 4096 bytes and leads to 'EHCI
timed out on TD' messages because of 'out of buffer pointers'
in ehci_td_buffer function.

The bug appears during load of a fragmented file and
read from or write to an unaligned memory address.

Cc: Marek Vasut <marex@denx.de>
Signed-off-by: Stefan Herbrechtsmeier <stefan@herbrechtsmeier.net>

---
Changes for v2:
 - Replace fixed worst case calculation with dynamic
   computation based on start address of transfer

 common/usb_storage.c |   37 ++++++++++++++++++++-----------------
 1 file changed, 20 insertions(+), 17 deletions(-)

Comments

Marek Vasut July 10, 2012, 2:12 a.m. UTC | #1
Dear Stefan Herbrechtsmeier,

> The commit 5dd95cf93dfffa1d19a1928990852aac9f55b9d9 'usb_storage:
> Fix EHCI "out of buffer pointers" with CD-ROM' introduce a bug in
> usb_storage as it wrongly assumes that every transfer can use
> 4096 bytes per qt_buffer. This is wrong if the start address of
> the data is not page aligned to 4096 bytes and leads to 'EHCI
> timed out on TD' messages because of 'out of buffer pointers'
> in ehci_td_buffer function.

Yes, this can be simply confirmed even with USB stick by loading to unaligned 
address. It'll make the buffers overflow too.

> The bug appears during load of a fragmented file and
> read from or write to an unaligned memory address.
> 
> Cc: Marek Vasut <marex@denx.de>
> Signed-off-by: Stefan Herbrechtsmeier <stefan@herbrechtsmeier.net>
> 
> ---
> Changes for v2:
>  - Replace fixed worst case calculation with dynamic
>    computation based on start address of transfer
> 
>  common/usb_storage.c |   37 ++++++++++++++++++++-----------------
>  1 file changed, 20 insertions(+), 17 deletions(-)
> 
> diff --git a/common/usb_storage.c b/common/usb_storage.c
> index faad237..bdc306f 100644
> --- a/common/usb_storage.c
> +++ b/common/usb_storage.c
> @@ -150,12 +150,17 @@ struct us_data {
>  	unsigned int	irqpipe;	 	/* pipe for release_irq */
>  	unsigned char	irqmaxp;		/* max packed for irq Pipe */
>  	unsigned char	irqinterval;		/* Intervall for IRQ Pipe */
> -	unsigned long	max_xfer_blk;		/* Max blocks per xfer */
>  	ccb		*srb;			/* current srb */
>  	trans_reset	transport_reset;	/* reset routine */
>  	trans_cmnd	transport;		/* transport routine */
>  };
> 
> +/*
> + * The U-Boot EHCI driver cannot handle more than 5 page aligned buffers
> + * of 4096 bytes in a transfer without running itself out of qt_buffers
> + */
> +#define USB_MAX_XFER_BLK(start, blksz)	(((4096 * 5) - (start % 4096)) /
> blksz) +

Can't something in include/common.h around line 900 can't be used?

btw put braces around (start) in the macro and around (blksz) .

[...]

The rest is good, thanks! :-)

Best regards,
Marek Vasut
Stefan Herbrechtsmeier July 10, 2012, 6:53 a.m. UTC | #2
Am 10.07.2012 04:12, schrieb Marek Vasut:
>> The commit 5dd95cf93dfffa1d19a1928990852aac9f55b9d9 'usb_storage:
>> Fix EHCI "out of buffer pointers" with CD-ROM' introduce a bug in
>> usb_storage as it wrongly assumes that every transfer can use
>> 4096 bytes per qt_buffer. This is wrong if the start address of
>> the data is not page aligned to 4096 bytes and leads to 'EHCI
>> timed out on TD' messages because of 'out of buffer pointers'
>> in ehci_td_buffer function.
> Yes, this can be simply confirmed even with USB stick by loading to unaligned
> address. It'll make the buffers overflow too.
>
>> The bug appears during load of a fragmented file and
>> read from or write to an unaligned memory address.
>>
>> Cc: Marek Vasut <marex@denx.de>
>> Signed-off-by: Stefan Herbrechtsmeier <stefan@herbrechtsmeier.net>
>>
>> ---
>> Changes for v2:
>>   - Replace fixed worst case calculation with dynamic
>>     computation based on start address of transfer
>>
>>   common/usb_storage.c |   37 ++++++++++++++++++++-----------------
>>   1 file changed, 20 insertions(+), 17 deletions(-)
>>
>> diff --git a/common/usb_storage.c b/common/usb_storage.c
>> index faad237..bdc306f 100644
>> --- a/common/usb_storage.c
>> +++ b/common/usb_storage.c
>> @@ -150,12 +150,17 @@ struct us_data {
>>   	unsigned int	irqpipe;	 	/* pipe for release_irq */
>>   	unsigned char	irqmaxp;		/* max packed for irq Pipe */
>>   	unsigned char	irqinterval;		/* Intervall for IRQ Pipe */
>> -	unsigned long	max_xfer_blk;		/* Max blocks per xfer */
>>   	ccb		*srb;			/* current srb */
>>   	trans_reset	transport_reset;	/* reset routine */
>>   	trans_cmnd	transport;		/* transport routine */
>>   };
>>
>> +/*
>> + * The U-Boot EHCI driver cannot handle more than 5 page aligned buffers
>> + * of 4096 bytes in a transfer without running itself out of qt_buffers
>> + */
>> +#define USB_MAX_XFER_BLK(start, blksz)	(((4096 * 5) - (start % 4096)) /
>> blksz) +
> Can't something in include/common.h around line 900 can't be used?
If you mean the round functions I don't need them, as I need the
leftover of 4096 and I need to divide round down the count.

> btw put braces around (start) in the macro and around (blksz) .
I will send a v3 tonight.

Regards,
     Stefan
Marek Vasut July 10, 2012, 7:22 a.m. UTC | #3
Dear Stefan Herbrechtsmeier,

> Am 10.07.2012 04:12, schrieb Marek Vasut:
> >> The commit 5dd95cf93dfffa1d19a1928990852aac9f55b9d9 'usb_storage:
> >> Fix EHCI "out of buffer pointers" with CD-ROM' introduce a bug in
> >> usb_storage as it wrongly assumes that every transfer can use
> >> 4096 bytes per qt_buffer. This is wrong if the start address of
> >> the data is not page aligned to 4096 bytes and leads to 'EHCI
> >> timed out on TD' messages because of 'out of buffer pointers'
> >> in ehci_td_buffer function.
> > 
> > Yes, this can be simply confirmed even with USB stick by loading to
> > unaligned address. It'll make the buffers overflow too.
> > 
> >> The bug appears during load of a fragmented file and
> >> read from or write to an unaligned memory address.
> >> 
> >> Cc: Marek Vasut <marex@denx.de>
> >> Signed-off-by: Stefan Herbrechtsmeier <stefan@herbrechtsmeier.net>
> >> 
> >> ---
> >> 
> >> Changes for v2:
> >>   - Replace fixed worst case calculation with dynamic
> >>   
> >>     computation based on start address of transfer
> >>   
> >>   common/usb_storage.c |   37 ++++++++++++++++++++-----------------
> >>   1 file changed, 20 insertions(+), 17 deletions(-)
> >> 
> >> diff --git a/common/usb_storage.c b/common/usb_storage.c
> >> index faad237..bdc306f 100644
> >> --- a/common/usb_storage.c
> >> +++ b/common/usb_storage.c
> >> @@ -150,12 +150,17 @@ struct us_data {
> >> 
> >>   	unsigned int	irqpipe;	 	/* pipe for release_irq */
> >>   	unsigned char	irqmaxp;		/* max packed for irq Pipe */
> >>   	unsigned char	irqinterval;		/* Intervall for IRQ Pipe */
> >> 
> >> -	unsigned long	max_xfer_blk;		/* Max blocks per xfer */
> >> 
> >>   	ccb		*srb;			/* current srb */
> >>   	trans_reset	transport_reset;	/* reset routine */
> >>   	trans_cmnd	transport;		/* transport routine */
> >>   
> >>   };
> >> 
> >> +/*
> >> + * The U-Boot EHCI driver cannot handle more than 5 page aligned
> >> buffers + * of 4096 bytes in a transfer without running itself out of
> >> qt_buffers + */
> >> +#define USB_MAX_XFER_BLK(start, blksz)	(((4096 * 5) - (start % 4096)) /
> >> blksz) +
> > 
> > Can't something in include/common.h around line 900 can't be used?
> 
> If you mean the round functions I don't need them, as I need the
> leftover of 4096 and I need to divide round down the count.
> 
> > btw put braces around (start) in the macro and around (blksz) .
> 
> I will send a v3 tonight.

Ok then, I think this is just perfect than and it should definitelly hit this 
release :-)

Thank you very much, sorry for pestering you too much and adding delays. Shame 
on my maintaining skills.

> Regards,
>      Stefan

Best regards,
Marek Vasut
diff mbox

Patch

diff --git a/common/usb_storage.c b/common/usb_storage.c
index faad237..bdc306f 100644
--- a/common/usb_storage.c
+++ b/common/usb_storage.c
@@ -150,12 +150,17 @@  struct us_data {
 	unsigned int	irqpipe;	 	/* pipe for release_irq */
 	unsigned char	irqmaxp;		/* max packed for irq Pipe */
 	unsigned char	irqinterval;		/* Intervall for IRQ Pipe */
-	unsigned long	max_xfer_blk;		/* Max blocks per xfer */
 	ccb		*srb;			/* current srb */
 	trans_reset	transport_reset;	/* reset routine */
 	trans_cmnd	transport;		/* transport routine */
 };
 
+/*
+ * The U-Boot EHCI driver cannot handle more than 5 page aligned buffers
+ * of 4096 bytes in a transfer without running itself out of qt_buffers
+ */
+#define USB_MAX_XFER_BLK(start, blksz)	(((4096 * 5) - (start % 4096)) / blksz)
+
 static struct us_data usb_stor[USB_MAX_STOR_DEV];
 
 
@@ -1041,7 +1046,7 @@  static void usb_bin_fixup(struct usb_device_descriptor descriptor,
 unsigned long usb_stor_read(int device, unsigned long blknr,
 			    unsigned long blkcnt, void *buffer)
 {
-	unsigned long start, blks, buf_addr;
+	unsigned long start, blks, buf_addr, max_xfer_blk;
 	unsigned short smallblks;
 	struct usb_device *dev;
 	struct us_data *ss;
@@ -1083,12 +1088,14 @@  unsigned long usb_stor_read(int device, unsigned long blknr,
 		/* XXX need some comment here */
 		retry = 2;
 		srb->pdata = (unsigned char *)buf_addr;
-		if (blks > ss->max_xfer_blk)
-			smallblks = ss->max_xfer_blk;
+		max_xfer_blk = USB_MAX_XFER_BLK(buf_addr,
+						usb_dev_desc[device].blksz);
+		if (blks > max_xfer_blk)
+			smallblks = (unsigned short) max_xfer_blk;
 		else
 			smallblks = (unsigned short) blks;
 retry_it:
-		if (smallblks == ss->max_xfer_blk)
+		if (smallblks == max_xfer_blk)
 			usb_show_progress();
 		srb->datalen = usb_dev_desc[device].blksz * smallblks;
 		srb->pdata = (unsigned char *)buf_addr;
@@ -1109,7 +1116,7 @@  retry_it:
 			start, smallblks, buf_addr);
 
 	usb_disable_asynch(0); /* asynch transfer allowed */
-	if (blkcnt >= ss->max_xfer_blk)
+	if (blkcnt >= max_xfer_blk)
 		debug("\n");
 	return blkcnt;
 }
@@ -1117,7 +1124,7 @@  retry_it:
 unsigned long usb_stor_write(int device, unsigned long blknr,
 				unsigned long blkcnt, const void *buffer)
 {
-	unsigned long start, blks, buf_addr;
+	unsigned long start, blks, buf_addr, max_xfer_blk;
 	unsigned short smallblks;
 	struct usb_device *dev;
 	struct us_data *ss;
@@ -1162,12 +1169,14 @@  unsigned long usb_stor_write(int device, unsigned long blknr,
 		 */
 		retry = 2;
 		srb->pdata = (unsigned char *)buf_addr;
-		if (blks > ss->max_xfer_blk)
-			smallblks = ss->max_xfer_blk;
+		max_xfer_blk = USB_MAX_XFER_BLK(buf_addr,
+						usb_dev_desc[device].blksz);
+		if (blks > max_xfer_blk)
+			smallblks = (unsigned short) max_xfer_blk;
 		else
 			smallblks = (unsigned short) blks;
 retry_it:
-		if (smallblks == ss->max_xfer_blk)
+		if (smallblks == max_xfer_blk)
 			usb_show_progress();
 		srb->datalen = usb_dev_desc[device].blksz * smallblks;
 		srb->pdata = (unsigned char *)buf_addr;
@@ -1188,7 +1197,7 @@  retry_it:
 			start, smallblks, buf_addr);
 
 	usb_disable_asynch(0); /* asynch transfer allowed */
-	if (blkcnt >= ss->max_xfer_blk)
+	if (blkcnt >= max_xfer_blk)
 		debug("\n");
 	return blkcnt;
 
@@ -1415,12 +1424,6 @@  int usb_stor_get_info(struct usb_device *dev, struct us_data *ss,
 	USB_STOR_PRINTF(" address %d\n", dev_desc->target);
 	USB_STOR_PRINTF("partype: %d\n", dev_desc->part_type);
 
-	/*
-	 * The U-Boot EHCI driver cannot handle more than 4096 * 5 bytes in a
-	 * transfer without running itself out of qt_buffers.
-	 */
-	ss->max_xfer_blk = (4096 * 5) / dev_desc->blksz;
-
 	init_part(dev_desc);
 
 	USB_STOR_PRINTF("partype: %d\n", dev_desc->part_type);