usb: chipidea: Simplify Tegra DMA alignment code
authorMichał Mirosław <mirq-linux@rere.qmqm.pl>
Thu, 28 Sep 2023 21:06:03 +0000 (23:06 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 20 Nov 2023 10:59:26 +0000 (11:59 +0100)
[ Upstream commit 2ae61a2562c0d1720545b0845829a65fb6a9c2c6 ]

The USB host on Tegra3 works with 32-bit alignment. Previous code tried
to align the buffer, but it did align the wrapper struct instead, so
the buffer was at a constant offset of 8 bytes (two pointers) from
expected alignment.  Since kmalloc() guarantees at least 8-byte
alignment already, the alignment-extending is removed.

Fixes: fc53d5279094 ("usb: chipidea: tegra: Support host mode")
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Link: https://lore.kernel.org/r/a0d917d492b1f91ee0019e68b8e8bca9c585393f.1695934946.git.mirq-linux@rere.qmqm.pl
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/usb/chipidea/host.c

index abddd39..0cce192 100644 (file)
@@ -30,8 +30,7 @@ struct ehci_ci_priv {
 };
 
 struct ci_hdrc_dma_aligned_buffer {
-       void *kmalloc_ptr;
-       void *old_xfer_buffer;
+       void *original_buffer;
        u8 data[];
 };
 
@@ -380,60 +379,52 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
        return 0;
 }
 
-static void ci_hdrc_free_dma_aligned_buffer(struct urb *urb)
+static void ci_hdrc_free_dma_aligned_buffer(struct urb *urb, bool copy_back)
 {
        struct ci_hdrc_dma_aligned_buffer *temp;
-       size_t length;
 
        if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
                return;
+       urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
 
        temp = container_of(urb->transfer_buffer,
                            struct ci_hdrc_dma_aligned_buffer, data);
+       urb->transfer_buffer = temp->original_buffer;
+
+       if (copy_back && usb_urb_dir_in(urb)) {
+               size_t length;
 
-       if (usb_urb_dir_in(urb)) {
                if (usb_pipeisoc(urb->pipe))
                        length = urb->transfer_buffer_length;
                else
                        length = urb->actual_length;
 
-               memcpy(temp->old_xfer_buffer, temp->data, length);
+               memcpy(temp->original_buffer, temp->data, length);
        }
-       urb->transfer_buffer = temp->old_xfer_buffer;
-       kfree(temp->kmalloc_ptr);
 
-       urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
+       kfree(temp);
 }
 
 static int ci_hdrc_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
 {
-       struct ci_hdrc_dma_aligned_buffer *temp, *kmalloc_ptr;
-       const unsigned int ci_hdrc_usb_dma_align = 32;
-       size_t kmalloc_size;
+       struct ci_hdrc_dma_aligned_buffer *temp;
 
        if (urb->num_sgs || urb->sg || urb->transfer_buffer_length == 0)
                return 0;
-       if (!((uintptr_t)urb->transfer_buffer & (ci_hdrc_usb_dma_align - 1)) && !(urb->transfer_buffer_length & 3))
+       if (IS_ALIGNED((uintptr_t)urb->transfer_buffer, 4)
+           && IS_ALIGNED(urb->transfer_buffer_length, 4))
                return 0;
 
-       /* Allocate a buffer with enough padding for alignment */
-       kmalloc_size = ALIGN(urb->transfer_buffer_length, 4) +
-                      sizeof(struct ci_hdrc_dma_aligned_buffer) +
-                      ci_hdrc_usb_dma_align - 1;
-
-       kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
-       if (!kmalloc_ptr)
+       temp = kmalloc(sizeof(*temp) + ALIGN(urb->transfer_buffer_length, 4), mem_flags);
+       if (!temp)
                return -ENOMEM;
 
-       /* Position our struct dma_aligned_buffer such that data is aligned */
-       temp = PTR_ALIGN(kmalloc_ptr + 1, ci_hdrc_usb_dma_align) - 1;
-       temp->kmalloc_ptr = kmalloc_ptr;
-       temp->old_xfer_buffer = urb->transfer_buffer;
        if (usb_urb_dir_out(urb))
                memcpy(temp->data, urb->transfer_buffer,
                       urb->transfer_buffer_length);
-       urb->transfer_buffer = temp->data;
 
+       temp->original_buffer = urb->transfer_buffer;
+       urb->transfer_buffer = temp->data;
        urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
 
        return 0;
@@ -450,7 +441,7 @@ static int ci_hdrc_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
 
        ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
        if (ret)
-               ci_hdrc_free_dma_aligned_buffer(urb);
+               ci_hdrc_free_dma_aligned_buffer(urb, false);
 
        return ret;
 }
@@ -458,7 +449,7 @@ static int ci_hdrc_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
 static void ci_hdrc_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
 {
        usb_hcd_unmap_urb_for_dma(hcd, urb);
-       ci_hdrc_free_dma_aligned_buffer(urb);
+       ci_hdrc_free_dma_aligned_buffer(urb, true);
 }
 
 #ifdef CONFIG_PM_SLEEP