From: Boris ARZUR <boris@konbu.org>
To: Guenter Roeck <linux@roeck-us.net>
Cc: linux-usb@vger.kernel.org,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Minas Harutyunyan <hminas@synopsys.com>,
William Wu <william.wu@rock-chips.com>,
Dmitry Torokhov <dmitry.torokhov@gmail.com>,
Douglas Anderson <dianders@chromium.org>,
a.seppala@gmail.com
Subject: Re: [PATCH] usb: dwc2: extend treatment for incomplete transfer
Date: Sun, 23 Feb 2020 21:02:47 +0900 [thread overview]
Message-ID: <20200223120247.GA21552@tungsten> (raw)
In-Reply-To: <20200219211056.GA829@roeck-us.net>
[-- Attachment #1: Type: text/plain, Size: 12945 bytes --]
Hi Guenter,
I tried your series of patch. rndis_host tethering & loading the machine
seems to work fine. No more crashing.
That being said, I now have an issue with regular USB keys (I tried a few):
usb 3-1: reset high-speed USB device number 2 using dwc2
I was able to reproduce this issue with the unpatched kernel, by disabling
the early return in dwc2_alloc_dma_aligned_buffer(), see attached.
There are times were re-allocation fails, either with your patch or with
the (almost-)original code.
In particular it seems that there is a packet of lenght 13, usb_urb_dir_in() == true,
usb_pipetype(urb->pipe) == PIPE_BULK, that comes in every 2s or so, that
does not reallocate properly.
In the original code, it's not a problem thanks to the early return, but your code
wants 512B (maxp) and forces reallocation...
Thanks, Boris.
Guenter Roeck wrote:
>On Sat, Feb 15, 2020 at 02:36:47PM +0900, Boris ARZUR wrote:
>> Hi Guenter,
>>
>> >> The first time around I was 0/ changing fonts 1/ trimming the dump message
>> >> in the kernel 2/ filming my screen. That's not practical at all...
>> Mmm, pstore does seem to work on my machine. CHROME_PSTORE is not available
>> to me because I'm not on x86, I just enabled the rest and nothing pops up
>> in /sys/fs/pstore...
>>
>> So I took pictures (OCR did not help):
>> - https://funyu.konbu.org/pQUF2P08etcpVxuKZ0A720%2b0/IMG_20200215_134343.jpg.webp
>> is a stack trace for your earlier patch "min_t", in
>> https://www.spinics.net/lists/linux-usb/msg191019.html ;
>> - https://funyu.konbu.org/pQUF2P08etcpVxuKZ0A720%2b0/IMG_20200215_135816.jpg.webp
>> is a stack trace for your later patch "container_of", in
>> https://www.spinics.net/lists/linux-usb/msg191074.html .
>>
>> Both patches crash (without even loading the machine), but "container_of" is
>> a bit more resilient.
>>
>
>Yes, those patches didn't address the core problem. Can you test with the
>attached two patches ?
>
>Thanks,
>Guenter
>From a1c0551b62b038b495177737828f986961184110 Mon Sep 17 00:00:00 2001
>From: Guenter Roeck <linux@roeck-us.net>
>Date: Mon, 10 Feb 2020 14:04:06 -0800
>Subject: [PATCH 1/2] usb: dwc2: Simplify DMA alignment code
>
>The code to align buffers for DMA was first introduced with commit
>3bc04e28a030 ("usb: dwc2: host: Get aligned DMA in a more supported way").
>It was updated with commit 56406e017a88 ("usb: dwc2: Fix DMA alignment
>to start at allocated boundary") because it did not really align buffers to
>DMA boundaries but to DWC2_USB_DMA_ALIGN. This was then optimized in commit
>1e111e885238 ("usb: dwc2: Fix inefficient copy of unaligned buffers")
>to only copy actual data rather than the whole buffer. Commit 4a4863bf2e79
>("usb: dwc2: Fix DMA cache alignment issues") changed this further to add
>a padding at the end of the buffer to ensure that the old data pointer is
>not in the same cache line as the buffer.
>
>This last commit states "Otherwise, the stored_xfer_buffer gets corrupted
>for IN URBs on non-cache-coherent systems". However, such corruptions are
>still observed. Either case, DMA is not expected to overwrite more memory
>than it is supposed to do, suggesting that the commit may have been hiding
>a problem rather than fixing it.
>
>On top of that, DMA alignment is still not guaranteed since it only happens
>if the original buffer is not aligned to DWC2_USB_DMA_ALIGN, which is still
>a constant of 4 and not really associated with DMA alignment.
>
>Move the old data pointer back to the beginning of the new buffer,
>restoring most of the original commit and to simplify the code. Define
>DWC2_USB_DMA_ALIGN to dma_get_cache_alignment() to fix the DMA alignment
>for real this time.
>
>Signed-off-by: Guenter Roeck <linux@roeck-us.net>
>---
> drivers/usb/dwc2/hcd.c | 50 +++++++++++++++++++-----------------------
> 1 file changed, 22 insertions(+), 28 deletions(-)
>
>diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
>index b90f858af960..b5841197165a 100644
>--- a/drivers/usb/dwc2/hcd.c
>+++ b/drivers/usb/dwc2/hcd.c
>@@ -2469,21 +2469,24 @@ static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
> return 0;
> }
>
>-#define DWC2_USB_DMA_ALIGN 4
>+#define DWC2_USB_DMA_ALIGN dma_get_cache_alignment()
>+
>+struct dma_aligned_buffer {
>+ void *kmalloc_ptr;
>+ void *old_xfer_buffer;
>+ u8 data[0];
>+};
>
> static void dwc2_free_dma_aligned_buffer(struct urb *urb)
> {
>- void *stored_xfer_buffer;
>+ struct dma_aligned_buffer *temp;
> size_t length;
>
> if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
> return;
>
>- /* Restore urb->transfer_buffer from the end of the allocated area */
>- memcpy(&stored_xfer_buffer,
>- PTR_ALIGN(urb->transfer_buffer + urb->transfer_buffer_length,
>- dma_get_cache_alignment()),
>- sizeof(urb->transfer_buffer));
>+ temp = container_of(urb->transfer_buffer,
>+ struct dma_aligned_buffer, data);
>
> if (usb_urb_dir_in(urb)) {
> if (usb_pipeisoc(urb->pipe))
>@@ -2491,17 +2494,17 @@ static void dwc2_free_dma_aligned_buffer(struct urb *urb)
> else
> length = urb->actual_length;
>
>- memcpy(stored_xfer_buffer, urb->transfer_buffer, length);
>+ memcpy(temp->old_xfer_buffer, temp->data, length);
> }
>- kfree(urb->transfer_buffer);
>- urb->transfer_buffer = stored_xfer_buffer;
>+ urb->transfer_buffer = temp->old_xfer_buffer;
>+ kfree(temp->kmalloc_ptr);
>
> urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
> }
>
> static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
> {
>- void *kmalloc_ptr;
>+ struct dma_aligned_buffer *temp, *kmalloc_ptr;
> size_t kmalloc_size;
>
> if (urb->num_sgs || urb->sg ||
>@@ -2509,31 +2512,22 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
> !((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
> return 0;
>
>- /*
>- * Allocate a buffer with enough padding for original transfer_buffer
>- * pointer. This allocation is guaranteed to be aligned properly for
>- * DMA
>- */
>+ /* Allocate a buffer with enough padding for alignment */
> kmalloc_size = urb->transfer_buffer_length +
>- (dma_get_cache_alignment() - 1) +
>- sizeof(urb->transfer_buffer);
>+ sizeof(struct dma_aligned_buffer) + DWC2_USB_DMA_ALIGN - 1;
>
> kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
> if (!kmalloc_ptr)
> return -ENOMEM;
>
>- /*
>- * Position value of original urb->transfer_buffer pointer to the end
>- * of allocation for later referencing
>- */
>- memcpy(PTR_ALIGN(kmalloc_ptr + urb->transfer_buffer_length,
>- dma_get_cache_alignment()),
>- &urb->transfer_buffer, sizeof(urb->transfer_buffer));
>-
>+ /* Position our struct dma_aligned_buffer such that data is aligned */
>+ temp = PTR_ALIGN(kmalloc_ptr + 1, DWC2_USB_DMA_ALIGN) - 1;
>+ temp->kmalloc_ptr = kmalloc_ptr;
>+ temp->old_xfer_buffer = urb->transfer_buffer;
> if (usb_urb_dir_out(urb))
>- memcpy(kmalloc_ptr, urb->transfer_buffer,
>+ memcpy(temp->data, urb->transfer_buffer,
> urb->transfer_buffer_length);
>- urb->transfer_buffer = kmalloc_ptr;
>+ urb->transfer_buffer = temp->data;
>
> urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
>
>--
>2.17.1
>
>From 9df13854b3717f8c16a2012dec614f737bb8c15d Mon Sep 17 00:00:00 2001
>From: Guenter Roeck <linux@roeck-us.net>
>Date: Mon, 10 Feb 2020 13:11:00 -0800
>Subject: [PATCH 2/2] usb: dwc2: Allocate input buffers as multiples of
> wMaxPacketSize
>MIME-Version: 1.0
>Content-Type: text/plain; charset=UTF-8
>Content-Transfer-Encoding: 8bit
>
>The following messages are seen on Veyron Chromebooks running v4.19 or
>later kernels.
>
>dwc2 ff580000.usb: dwc2_update_urb_state(): trimming xfer length
>dwc2 ff580000.usb: dwc2_hc_chhltd_intr_dma: Channel 7 - ChHltd set, but reason is unknown
>dwc2 ff580000.usb: hcint 0x00000002, intsts 0x04600021
>
>This is typically followed by a crash.
>
>Unable to handle kernel paging request at virtual address 29f9d9fc
>pgd = 4797dac9
>[29f9d9fc] *pgd=80000000004003, *pmd=00000000
>Internal error: Oops: a06 [#1] PREEMPT SMP ARM
>Modules linked in: ip6t_REJECT rfcomm i2c_dev uinput hci_uart btbcm ...
>CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 4.19.87-07825-g4ab3515f6e4d #1
>Hardware name: Rockchip (Device Tree)
>PC is at memcpy+0x50/0x330
>LR is at 0xdd9ac94
>...
>[<c0a89f50>] (memcpy) from [<c0783b94>] (dwc2_free_dma_aligned_buffer+0x5c/0x7c)
>[<c0783b94>] (dwc2_free_dma_aligned_buffer) from [<c0765dcc>] (__usb_hcd_giveback_urb+0x78/0x130)
>[<c0765dcc>] (__usb_hcd_giveback_urb) from [<c07678fc>] (usb_giveback_urb_bh+0xa0/0xe4)
>[<c07678fc>] (usb_giveback_urb_bh) from [<c023a164>] (tasklet_action_common+0xc0/0xdc)
>[<c023a164>] (tasklet_action_common) from [<c02021f0>] (__do_softirq+0x1b8/0x434)
>[<c02021f0>] (__do_softirq) from [<c0239a14>] (irq_exit+0xdc/0xe0)
>[<c0239a14>] (irq_exit) from [<c029f260>] (__handle_domain_irq+0x94/0xd0)
>[<c029f260>] (__handle_domain_irq) from [<c05da780>] (gic_handle_irq+0x74/0xb0)
>[<c05da780>] (gic_handle_irq) from [<c02019f8>] (__irq_svc+0x58/0x8c)
>
>The crash suggests that the memory after the end of a temporary DMA-aligned
>buffer is overwritten.
>
>The problem is typically only seen in kernels which include commit
>56406e017a88 ("usb: dwc2: Fix DMA alignment to start at allocated
>boundary"), presumably because that commit moves the pointer to the old
>buffer to the end of the newly allocated buffer, where it is more likely
>to be overwritten.
>
>Code analysis shows that the transfer size programmed into the chip for
>input transfers is a multiple of an endpoint's maximum packet size
>(wMaxPacketSize). In the observed situation, the transfer size and thus
>the size of the input buffer is 1522 bytes. With a maximum packet size
>of 64 bytes, the chip is programmed to receive up to 1536 bytes of data.
>This overwrites the end of the buffer.
>
>To work around the problem, always allocate a multiple of wMaxPacketSize
>bytes for receive buffers. Do this even for DMA-aligned buffers if the
>receive buffer size is not a multiple of wMaxPacketSize. At the same time,
>do not update chan->xfer_len if the transfer size is 0.
>
>Reported-by: Boris ARZUR <boris@konbu.org>
>Cc: Boris ARZUR <boris@konbu.org>
>Cc: Jonathan Bell <jonathan@raspberrypi.org>
>Cc: Antti Seppälä <a.seppala@gmail.com>
>Fixes: 56406e017a88 ("usb: dwc2: Fix DMA alignment to start at allocated boundary")
>Signed-off-by: Guenter Roeck <linux@roeck-us.net>
>---
> drivers/usb/dwc2/hcd.c | 37 +++++++++++++++++++++++++++----------
> 1 file changed, 27 insertions(+), 10 deletions(-)
>
>diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
>index b5841197165a..f27dc11e409c 100644
>--- a/drivers/usb/dwc2/hcd.c
>+++ b/drivers/usb/dwc2/hcd.c
>@@ -1313,18 +1313,20 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
> if (num_packets > max_hc_pkt_count) {
> num_packets = max_hc_pkt_count;
> chan->xfer_len = num_packets * chan->max_packet;
>+ } else if (chan->ep_is_in) {
>+ /*
>+ * Always program an integral # of max packets for IN
>+ * transfers.
>+ * Note: This assumes that the input buffer is
>+ * aligned accordingly.
>+ */
>+ chan->xfer_len = num_packets * chan->max_packet;
> }
> } else {
> /* Need 1 packet for transfer length of 0 */
> num_packets = 1;
> }
>
>- if (chan->ep_is_in)
>- /*
>- * Always program an integral # of max packets for IN
>- * transfers
>- */
>- chan->xfer_len = num_packets * chan->max_packet;
>
> if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
> chan->ep_type == USB_ENDPOINT_XFER_ISOC)
>@@ -2505,16 +2507,31 @@ static void dwc2_free_dma_aligned_buffer(struct urb *urb)
> static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
> {
> struct dma_aligned_buffer *temp, *kmalloc_ptr;
>+ struct usb_host_endpoint *ep = urb->ep;
>+ int maxp = usb_endpoint_maxp(&ep->desc);
> size_t kmalloc_size;
>
>- if (urb->num_sgs || urb->sg ||
>- urb->transfer_buffer_length == 0 ||
>+ if (urb->num_sgs || urb->sg || urb->transfer_buffer_length == 0)
>+ return 0;
>+
>+ /*
>+ * Input transfer buffer size must be a multiple of the endpoint's
>+ * maximum packet size to match the transfer limit programmed into
>+ * the chip.
>+ * See calculation of chan->xfer_len in dwc2_hc_start_transfer().
>+ */
>+ if (usb_urb_dir_out(urb))
>+ kmalloc_size = urb->transfer_buffer_length;
>+ else
>+ kmalloc_size = roundup(urb->transfer_buffer_length, maxp);
>+
>+ if (kmalloc_size == urb->transfer_buffer_length &&
> !((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
> return 0;
>
> /* Allocate a buffer with enough padding for alignment */
>- kmalloc_size = urb->transfer_buffer_length +
>- sizeof(struct dma_aligned_buffer) + DWC2_USB_DMA_ALIGN - 1;
>+ kmalloc_size += sizeof(struct dma_aligned_buffer) +
>+ DWC2_USB_DMA_ALIGN - 1;
>
> kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
> if (!kmalloc_ptr)
>--
>2.17.1
>
[-- Attachment #2: break_original.patch --]
[-- Type: text/plain, Size: 1197 bytes --]
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 2192a28..4c45642 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2503,12 +2503,27 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
{
void *kmalloc_ptr;
size_t kmalloc_size;
+ struct usb_host_endpoint *ep = urb->ep;
+ int maxp = usb_endpoint_maxp(&ep->desc);
if (urb->num_sgs || urb->sg ||
- urb->transfer_buffer_length == 0 ||
- !((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
+ urb->transfer_buffer_length == 0)
return 0;
+ if (!((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1))) {
+ //[ 8906.761517] EARLY RET len:13 out:0 in:1 type:3 mx:512
+ //[ 8908.776755] EARLY RET len:13 out:0 in:1 type:3 mx:512
+ if (urb->transfer_buffer_length == 13) {
+ printk("EARLY RET len:%u out:%d in:%d type:%d mx:%d ",
+ urb->transfer_buffer_length,
+ usb_urb_dir_out(urb) ? 1 : 0,
+ usb_urb_dir_in(urb) ? 1 : 0,
+ usb_pipetype(urb->pipe),
+ maxp);
+ return 0;
+ }
+ }
+
/*
* Allocate a buffer with enough padding for original transfer_buffer
* pointer. This allocation is guaranteed to be aligned properly for
next prev parent reply other threads:[~2020-02-23 12:03 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-02-10 21:39 [PATCH] usb: dwc2: extend treatment for incomplete transfer Guenter Roeck
2020-02-11 5:49 ` Boris ARZUR
2020-02-11 13:26 ` Guenter Roeck
2020-02-11 16:15 ` Guenter Roeck
2020-02-15 5:36 ` Boris ARZUR
2020-02-19 21:10 ` Guenter Roeck
2020-02-23 11:00 ` Antti Seppälä
2020-02-23 12:10 ` Boris ARZUR
2020-02-23 13:45 ` Guenter Roeck
2020-02-23 18:20 ` Antti Seppälä
2020-02-23 18:47 ` Guenter Roeck
2020-02-23 12:02 ` Boris ARZUR [this message]
2020-02-23 13:53 ` Guenter Roeck
2020-02-25 0:18 ` Guenter Roeck
2020-02-20 21:22 ` Guenter Roeck
-- strict thread matches above, loose matches on Subject: below --
2019-11-05 3:29 Boris ARZUR
2019-11-05 3:39 ` Boris ARZUR
2020-01-31 22:09 ` Guenter Roeck
2020-02-02 5:15 ` Boris ARZUR
2020-02-02 18:52 ` Guenter Roeck
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200223120247.GA21552@tungsten \
--to=boris@konbu.org \
--cc=a.seppala@gmail.com \
--cc=dianders@chromium.org \
--cc=dmitry.torokhov@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=hminas@synopsys.com \
--cc=linux-usb@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=william.wu@rock-chips.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).