Message ID | a0d917d492b1f91ee0019e68b8e8bca9c585393f.1695934946.git.mirq-linux@rere.qmqm.pl (mailing list archive) |
---|---|
State | Accepted |
Commit | 2ae61a2562c0d1720545b0845829a65fb6a9c2c6 |
Headers | show |
Series | usb: chipidea: An USB DMA fix + cleanups for Tegra | expand |
On Thu, Sep 28, 2023 at 11:06:03PM +0200, Michał Mirosław wrote: > 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> > --- > drivers/usb/chipidea/host.c | 45 +++++++++++++++---------------------- > 1 file changed, 18 insertions(+), 27 deletions(-) > > diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c > index abddd39d1ff1..0cce19208370 100644 > --- a/drivers/usb/chipidea/host.c > +++ b/drivers/usb/chipidea/host.c > @@ -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; This threw me off a bit until I realized it was already there previously, just in a different place. Is there a particular reason why this is moved? Regardless, this looks fine, so: Acked-by: Thierry Reding <treding@nvidia.com> > > 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); > + > + 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 > -- > 2.39.2 >
On 23-10-11 23:53:28, Thierry Reding wrote: > On Thu, Sep 28, 2023 at 11:06:03PM +0200, Michał Mirosław wrote: > > 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> > > --- > > drivers/usb/chipidea/host.c | 45 +++++++++++++++---------------------- > > 1 file changed, 18 insertions(+), 27 deletions(-) > > > > diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c > > index abddd39d1ff1..0cce19208370 100644 > > --- a/drivers/usb/chipidea/host.c > > +++ b/drivers/usb/chipidea/host.c > > @@ -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; > > This threw me off a bit until I realized it was already there > previously, just in a different place. Is there a particular reason why > this is moved? > > Regardless, this looks fine, so: > > Acked-by: Thierry Reding <treding@nvidia.com> Acked-by: Peter Chen <peter.chen@kernel.org>
On Wed, Oct 11, 2023 at 11:53:28PM +0200, Thierry Reding wrote: > On Thu, Sep 28, 2023 at 11:06:03PM +0200, Michał Mirosław wrote: > > 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> > > --- > > drivers/usb/chipidea/host.c | 45 +++++++++++++++---------------------- > > 1 file changed, 18 insertions(+), 27 deletions(-) > > > > diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c > > index abddd39d1ff1..0cce19208370 100644 > > --- a/drivers/usb/chipidea/host.c > > +++ b/drivers/usb/chipidea/host.c > > @@ -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; > > This threw me off a bit until I realized it was already there > previously, just in a different place. Is there a particular reason why > this is moved? I figured that it is easier to understand if the test and clear of the URB_ALIGNED_TEMP_BUFFER bit is in a single place. Seeing it again, I think it could be replaced with __test_and_clear_bit() in a future commit. Best Regards Michał Mirosław
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index abddd39d1ff1..0cce19208370 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -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); + + 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
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> --- drivers/usb/chipidea/host.c | 45 +++++++++++++++---------------------- 1 file changed, 18 insertions(+), 27 deletions(-)