kernel: fix dwc2 (usb) dma crashes on RPi1-3
authorArne Fitzenreiter <arne_f@ipfire.org>
Mon, 10 Dec 2018 19:45:54 +0000 (20:45 +0100)
committerArne Fitzenreiter <arne_f@ipfire.org>
Mon, 10 Dec 2018 19:45:54 +0000 (20:45 +0100)
Signed-off-by: Arne Fitzenreiter <arne_f@ipfire.org>
lfs/linux
src/patches/linux/linux-4.14-Revert-usb-dwc2-Fix-DMA-alignment.patch [new file with mode: 0644]

index 0994e3e..3463788 100644 (file)
--- a/lfs/linux
+++ b/lfs/linux
@@ -157,10 +157,13 @@ endif
 ifeq "$(KCFG)" "-multi"
        # Apply Arm-multiarch kernel patches.
        cd $(DIR_APP) && xzcat $(DIR_DL)/arm-multi-patches-$(ARM_PATCHES).patch.xz | patch -Np1
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux/linux-4.14-Revert-usb-dwc2-Fix-DMA-alignment.patch
+
 endif
 ifeq "$(BUILD_ARCH)" "aarch64"
        # Apply Arm-multiarch kernel patches.
        cd $(DIR_APP) && xzcat $(DIR_DL)/arm-multi-patches-$(ARM_PATCHES).patch.xz | patch -Np1
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux/linux-4.14-Revert-usb-dwc2-Fix-DMA-alignment.patch
 endif
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux/linux-3.14.79-amba-fix.patch
 
diff --git a/src/patches/linux/linux-4.14-Revert-usb-dwc2-Fix-DMA-alignment.patch b/src/patches/linux/linux-4.14-Revert-usb-dwc2-Fix-DMA-alignment.patch
new file mode 100644 (file)
index 0000000..e4c8b99
--- /dev/null
@@ -0,0 +1,99 @@
+From a44147a09baf8c46cc0b02332df3a4656e0659d5 Mon Sep 17 00:00:00 2001
+From: Arne Fitzenreiter <arne_f@ipfire.org>
+Date: Mon, 10 Dec 2018 13:12:00 +0100
+Subject: [PATCH] Revert "usb: dwc2: Fix DMA alignment to start at allocated
+ boundary"
+
+This reverts commit 68fc92a0f3913d539d1ac68a861f895e34099e46.
+---
+ drivers/usb/dwc2/hcd.c | 44 +++++++++++++++++++++-----------------------
+ 1 file changed, 21 insertions(+), 23 deletions(-)
+
+diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
+index fa20ec4..4b81d08 100644
+--- a/drivers/usb/dwc2/hcd.c
++++ b/drivers/usb/dwc2/hcd.c
+@@ -2644,29 +2644,34 @@ static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
+ #define DWC2_USB_DMA_ALIGN 4
++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;
+       if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
+               return;
+-      /* Restore urb->transfer_buffer from the end of the allocated area */
+-      memcpy(&stored_xfer_buffer, urb->transfer_buffer +
+-             urb->transfer_buffer_length, sizeof(urb->transfer_buffer));
++      temp = container_of(urb->transfer_buffer,
++                          struct dma_aligned_buffer, data);
+       if (usb_urb_dir_in(urb))
+-              memcpy(stored_xfer_buffer, urb->transfer_buffer,
++              memcpy(temp->old_xfer_buffer, temp->data,
+                      urb->transfer_buffer_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 ||
+@@ -2674,29 +2679,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 +
+-              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(kmalloc_ptr + urb->transfer_buffer_length,
+-             &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.7.4
+