Skip to content

v2 dwc2 DMA alignment_buffer handling #6923

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 24, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions drivers/usb/dwc2/hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -2447,7 +2447,7 @@ static void dwc2_free_dma_aligned_buffer(struct urb *urb)

/* Restore urb->transfer_buffer from the end of the allocated area */
memcpy(&stored_xfer_buffer,
PTR_ALIGN(urb->transfer_buffer + urb->transfer_buffer_length,
PTR_ALIGN(urb->transfer_buffer + urb->transfer_buffer_length + DWC2_USB_DMA_ALIGN,
dma_get_cache_alignment()),
sizeof(urb->transfer_buffer));

Expand All @@ -2469,31 +2469,37 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
{
void *kmalloc_ptr;
size_t kmalloc_size;
bool small_ctrl;

if (urb->num_sgs || urb->sg || urb->transfer_buffer_length == 0)
return 0;

/*
* Hardware bug: small IN packets with length < 4 cause a
* 4-byte write to memory. This is only an issue for drivers that
* insist on packing a device's various properties into a struct
* and filling them one at a time with Control transfers (uvcvideo).
* Force the use of align_buf so that the subsequent memcpy puts
* the right number of bytes in the URB's buffer.
* Hardware bug: the core will only do DMA writes of full words
* in length, and DMA buffers must start at a word boundary.
* TODO: is this limited to BCM2835 and friends, or other core variants?
*/
small_ctrl = (urb->setup_packet &&
le16_to_cpu(((struct usb_ctrlrequest *)(urb->setup_packet))->wLength) < 4);

if (!small_ctrl && !((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
if (!(usb_urb_dir_in(urb) && (urb->transfer_buffer_length & (DWC2_USB_DMA_ALIGN - 1))) &&
!((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
return 0;

/*
* If the URB already has a DMA mapping, this alignment mechanism won't
* work - the replacement buffer won't be used by the core, as the HCD layer
* skips mapping. Mappings have the granularity of a page, so it's unlikely that the
* DMA length bug will cause data trampling. In any case, warn if there's a driver
* submitting unaligned mapped buffers.
*/
if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) {
if (urb->transfer_dma & (DWC2_USB_DMA_ALIGN - 1))
WARN_ONCE(1, "Unaligned DMA-mapped buffer");
return 0;
}
/*
* Allocate a buffer with enough padding for original transfer_buffer
* pointer. This allocation is guaranteed to be aligned properly for
* DMA
*/
kmalloc_size = urb->transfer_buffer_length +
kmalloc_size = urb->transfer_buffer_length + DWC2_USB_DMA_ALIGN +
(dma_get_cache_alignment() - 1) +
sizeof(urb->transfer_buffer);

Expand All @@ -2505,7 +2511,7 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
* 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,
memcpy(PTR_ALIGN(kmalloc_ptr + urb->transfer_buffer_length + DWC2_USB_DMA_ALIGN,
dma_get_cache_alignment()),
&urb->transfer_buffer, sizeof(urb->transfer_buffer));

Expand Down
Loading