linux-usb.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Guenter Roeck <linux@roeck-us.net>
To: Boris ARZUR <boris@konbu.org>
Cc: linux-usb@vger.kernel.org,
	FelipeBalbi <felipe.balbi@linux.intel.com>,
	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>
Subject: Re: [PATCH] usb: dwc2: extend treatment for incomplete transfer
Date: Tue, 11 Feb 2020 08:15:22 -0800	[thread overview]
Message-ID: <20200211161522.GA1894@roeck-us.net> (raw)
In-Reply-To: <20200211054953.GA2401@tungsten>

Hi Boris,

On Tue, Feb 11, 2020 at 02:49:53PM +0900, Boris ARZUR wrote:
> Hello Guenter,
> 
> >In the meantime, can you by any chance test the attached patch ? It _might_
> >fix the problem, but it is a bit of a wild shot.
> I tried your patch, but the machine does not finish booting.
> 
> 
> I would like to give you a dump, but the screen scrolls fast, and what's
> left when paused is not interesting. How do I get it to dump on disk?
> 
> My journalctl doesn't show anything. I have no kmesg.log anywhere.
> 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...
> 
> 
> I have been looking a bit at things. I believe that part of the issue
> is the need to re-align the buffer we get in the URB. I'm wondering if asking
> for a specific alignment when creating the URB could be doable.
> 
> 
> As a stop-gap, maybe doing things like in tegra ehci could fix our bug:
> https://github.com/torvalds/linux/blob/master/drivers/usb/host/ehci-tegra.c#L288
> i.e. having the old pointer before the new buffer instead of at the end of
> it.
> 
> Now if something is overwriting the buffer end, that would also be hiding the
> issue... but if the bug is related to lengths that don't match between
> allocation and free, that could work. In this case, that would also be
> hiding the issue :)
> 
See below for a patch (untested) doing just that. It may solve your immediate
problem, though it would still suffer from the buffer end overwrite.

> 
> >Unfortunately, I have been unable to reproduce the problem. It is seen only
> >with certain phones and with certain Ethernet adapters, and I was unable
> >to get any of those. I'll keep trying.
> If you want, I can run a kernel with some printk instrumentation or run
> experiments. I'll research a bit on how to get that kernel oops data, that

Unfortunately I have no real idea what to look out for. The problem may be
related to the phone sending more than one Ethernet packet in a single USB
transfer. See rndis_rx_fixup() for how that is handled in the driver.
I don't know how that would result in the observed problem, though.

Thanks,
Guenter

---
From 8efa9c598f2390dca2e97cbbe41e981caba41ca1 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] 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 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 2c81b346b464..9e04b3a314eb 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2470,21 +2470,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))
@@ -2492,17 +2495,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 ||
@@ -2510,31 +2513,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


  parent reply	other threads:[~2020-02-11 16:15 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 [this message]
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
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=20200211161522.GA1894@roeck-us.net \
    --to=linux@roeck-us.net \
    --cc=boris@konbu.org \
    --cc=dianders@chromium.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=felipe.balbi@linux.intel.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=hminas@synopsys.com \
    --cc=linux-usb@vger.kernel.org \
    --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).