* [PATCH v4 1/3] usb: core: add dma-noncoherent buffer alloc and free API
2025-07-03 10:38 [PATCH v4 0/3] add dma noncoherent API Xu Yang
@ 2025-07-03 10:38 ` Xu Yang
2025-07-03 13:46 ` Andy Shevchenko
2025-07-03 14:35 ` Alan Stern
2025-07-03 10:38 ` [PATCH v4 2/3] media: uvcvideo: use usb_alloc_noncoherent/usb_free_noncoherent() Xu Yang
2025-07-03 10:38 ` [PATCH v4 3/3] media: stk1160: " Xu Yang
2 siblings, 2 replies; 9+ messages in thread
From: Xu Yang @ 2025-07-03 10:38 UTC (permalink / raw)
To: ezequiel, mchehab, laurent.pinchart, hdegoede, gregkh, xu.yang_2,
mingo, tglx, andriy.shevchenko, viro, thomas.weissschuh
Cc: linux-media, linux-kernel, linux-usb, imx, jun.li
This will add usb_alloc_noncoherent() and usb_free_noncoherent()
functions to support alloc and free buffer in a dma-noncoherent way.
To explicit manage the memory ownership for the kernel and device,
this will also add usb_dma_noncoherent_sync_for_cpu/device() functions
and call it at proper time. The management requires the user save
sg_table returned by usb_alloc_noncoherent() to urb->sgt.
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
Changes in v4:
- improve if-else logic
Changes in v3:
- put Return section at the end of description
- correct some abbreviations
- remove usb_dma_noncoherent_sync_for_cpu() and
usb_dma_noncoherent_sync_for_device()
- do DMA sync in usb_hcd_map_urb_for_dma() and
usb_hcd_unmap_urb_for_dma()
- call flush_kernel_vmap_range() for OUT transfers
and invalidate_kernel_vmap_range() for IN transfers
---
drivers/usb/core/hcd.c | 33 ++++++++++++-----
drivers/usb/core/usb.c | 80 ++++++++++++++++++++++++++++++++++++++++++
include/linux/usb.h | 11 ++++++
3 files changed, 116 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index c22de97432a0..42d9d8db0968 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1342,29 +1342,35 @@ void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
if (IS_ENABLED(CONFIG_HAS_DMA) &&
- (urb->transfer_flags & URB_DMA_MAP_SG))
+ (urb->transfer_flags & URB_DMA_MAP_SG)) {
dma_unmap_sg(hcd->self.sysdev,
urb->sg,
urb->num_sgs,
dir);
- else if (IS_ENABLED(CONFIG_HAS_DMA) &&
- (urb->transfer_flags & URB_DMA_MAP_PAGE))
+ } else if (IS_ENABLED(CONFIG_HAS_DMA) &&
+ (urb->transfer_flags & URB_DMA_MAP_PAGE)) {
dma_unmap_page(hcd->self.sysdev,
urb->transfer_dma,
urb->transfer_buffer_length,
dir);
- else if (IS_ENABLED(CONFIG_HAS_DMA) &&
- (urb->transfer_flags & URB_DMA_MAP_SINGLE))
+ } else if (IS_ENABLED(CONFIG_HAS_DMA) &&
+ (urb->transfer_flags & URB_DMA_MAP_SINGLE)) {
dma_unmap_single(hcd->self.sysdev,
urb->transfer_dma,
urb->transfer_buffer_length,
dir);
- else if (urb->transfer_flags & URB_MAP_LOCAL)
+ } else if (urb->transfer_flags & URB_MAP_LOCAL) {
hcd_free_coherent(urb->dev->bus,
&urb->transfer_dma,
&urb->transfer_buffer,
urb->transfer_buffer_length,
dir);
+ } else if ((urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) && urb->sgt) {
+ dma_sync_sgtable_for_cpu(hcd->self.sysdev, urb->sgt, dir);
+ if (dir == DMA_FROM_DEVICE)
+ invalidate_kernel_vmap_range(urb->transfer_buffer,
+ urb->transfer_buffer_length);
+ }
/* Make it safe to call this routine more than once */
urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE |
@@ -1425,8 +1431,10 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
}
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- if (urb->transfer_buffer_length != 0
- && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ if (!urb->transfer_buffer_length)
+ return ret;
+
if (hcd->localmem_pool) {
ret = hcd_alloc_coherent(
urb->dev->bus, mem_flags,
@@ -1491,7 +1499,16 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE |
URB_SETUP_MAP_LOCAL)))
usb_hcd_unmap_urb_for_dma(hcd, urb);
+ } else {
+ if (!urb->sgt)
+ return ret;
+
+ if (dir == DMA_TO_DEVICE)
+ flush_kernel_vmap_range(urb->transfer_buffer,
+ urb->transfer_buffer_length);
+ dma_sync_sgtable_for_device(hcd->self.sysdev, urb->sgt, dir);
}
+
return ret;
}
EXPORT_SYMBOL_GPL(usb_hcd_map_urb_for_dma);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 118fa4c93a79..fca7735fc660 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -1030,6 +1030,86 @@ void usb_free_coherent(struct usb_device *dev, size_t size, void *addr,
}
EXPORT_SYMBOL_GPL(usb_free_coherent);
+/**
+ * usb_alloc_noncoherent - allocate dma-noncoherent buffer for URB_NO_xxx_DMA_MAP
+ * @dev: device the buffer will be used with
+ * @size: requested buffer size
+ * @mem_flags: affect whether allocation may block
+ * @dma: used to return DMA address of buffer
+ * @dir: DMA transfer direction
+ * @table: used to return sg_table of allocated memory
+ *
+ * To explicit manage the memory ownership for the kernel vs the device by
+ * USB core, the user needs save sg_table to urb->sgt. Then USB core will
+ * do DMA sync for CPU and device properly.
+ *
+ * When the buffer is no longer used, free it with usb_free_noncoherent().
+ *
+ * Return: Either null (indicating no buffer could be allocated), or the
+ * cpu-space pointer to a buffer that may be used to perform DMA to the
+ * specified device. Such cpu-space buffers are returned along with the DMA
+ * address (through the pointer provided).
+ */
+void *usb_alloc_noncoherent(struct usb_device *dev, size_t size,
+ gfp_t mem_flags, dma_addr_t *dma,
+ enum dma_data_direction dir,
+ struct sg_table **table)
+{
+ struct device *dmadev;
+ struct sg_table *sgt;
+ void *buffer;
+
+ if (!dev || !dev->bus)
+ return NULL;
+
+ dmadev = bus_to_hcd(dev->bus)->self.sysdev;
+
+ sgt = dma_alloc_noncontiguous(dmadev, size, dir, mem_flags, 0);
+ if (!sgt)
+ return NULL;
+
+ buffer = dma_vmap_noncontiguous(dmadev, size, sgt);
+ if (!buffer) {
+ dma_free_noncontiguous(dmadev, size, sgt, dir);
+ return NULL;
+ }
+
+ *table = sgt;
+ *dma = sg_dma_address(sgt->sgl);
+
+ return buffer;
+}
+EXPORT_SYMBOL_GPL(usb_alloc_noncoherent);
+
+/**
+ * usb_free_noncoherent - free memory allocated with usb_alloc_noncoherent()
+ * @dev: device the buffer was used with
+ * @size: requested buffer size
+ * @addr: CPU address of buffer
+ * @dir: DMA transfer direction
+ * @table: describe the allocated and DMA mapped memory,
+ *
+ * This reclaims an I/O buffer, letting it be reused. The memory must have
+ * been allocated using usb_alloc_noncoherent(), and the parameters must match
+ * those provided in that allocation request.
+ */
+void usb_free_noncoherent(struct usb_device *dev, size_t size,
+ void *addr, enum dma_data_direction dir,
+ struct sg_table *table)
+{
+ struct device *dmadev;
+
+ if (!dev || !dev->bus)
+ return;
+ if (!addr)
+ return;
+
+ dmadev = bus_to_hcd(dev->bus)->self.sysdev;
+ dma_vunmap_noncontiguous(dmadev, addr);
+ dma_free_noncontiguous(dmadev, size, table, dir);
+}
+EXPORT_SYMBOL_GPL(usb_free_noncoherent);
+
/*
* Notifications of device and interface registration
*/
diff --git a/include/linux/usb.h b/include/linux/usb.h
index e8662843e68c..9ade441ab4c8 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1619,6 +1619,7 @@ struct urb {
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
struct scatterlist *sg; /* (in) scatter gather buffer list */
+ struct sg_table *sgt; /* (in) scatter gather table for noncoherent buffer */
int num_mapped_sgs; /* (internal) mapped sg entries */
int num_sgs; /* (in) number of entries in the sg list */
u32 transfer_buffer_length; /* (in) data buffer length */
@@ -1824,6 +1825,16 @@ void *usb_alloc_coherent(struct usb_device *dev, size_t size,
void usb_free_coherent(struct usb_device *dev, size_t size,
void *addr, dma_addr_t dma);
+enum dma_data_direction;
+
+void *usb_alloc_noncoherent(struct usb_device *dev, size_t size,
+ gfp_t mem_flags, dma_addr_t *dma,
+ enum dma_data_direction dir,
+ struct sg_table **table);
+void usb_free_noncoherent(struct usb_device *dev, size_t size,
+ void *addr, enum dma_data_direction dir,
+ struct sg_table *table);
+
/*-------------------------------------------------------------------*
* SYNCHRONOUS CALL SUPPORT *
*-------------------------------------------------------------------*/
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v4 1/3] usb: core: add dma-noncoherent buffer alloc and free API
2025-07-03 10:38 ` [PATCH v4 1/3] usb: core: add dma-noncoherent buffer alloc and free API Xu Yang
@ 2025-07-03 13:46 ` Andy Shevchenko
2025-07-04 8:15 ` Xu Yang
2025-07-03 14:35 ` Alan Stern
1 sibling, 1 reply; 9+ messages in thread
From: Andy Shevchenko @ 2025-07-03 13:46 UTC (permalink / raw)
To: Xu Yang
Cc: ezequiel, mchehab, laurent.pinchart, hdegoede, gregkh, mingo,
tglx, viro, thomas.weissschuh, linux-media, linux-kernel,
linux-usb, imx, jun.li
On Thu, Jul 03, 2025 at 06:38:09PM +0800, Xu Yang wrote:
> This will add usb_alloc_noncoherent() and usb_free_noncoherent()
> functions to support alloc and free buffer in a dma-noncoherent way.
>
> To explicit manage the memory ownership for the kernel and device,
> this will also add usb_dma_noncoherent_sync_for_cpu/device() functions
> and call it at proper time. The management requires the user save
> sg_table returned by usb_alloc_noncoherent() to urb->sgt.
...
> dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> - if (urb->transfer_buffer_length != 0
> - && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
> + if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
> + if (!urb->transfer_buffer_length)
> + return ret;
This return ret out of a sudden are quite fragile. Please, move to use return 0
directly where it is supposed to be 0.
...
> + } else {
> + if (!urb->sgt)
> + return ret;
Ditto.
> + if (dir == DMA_TO_DEVICE)
> + flush_kernel_vmap_range(urb->transfer_buffer,
> + urb->transfer_buffer_length);
> + dma_sync_sgtable_for_device(hcd->self.sysdev, urb->sgt, dir);
> }
> return ret;
Ditto?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v4 1/3] usb: core: add dma-noncoherent buffer alloc and free API
2025-07-03 13:46 ` Andy Shevchenko
@ 2025-07-04 8:15 ` Xu Yang
0 siblings, 0 replies; 9+ messages in thread
From: Xu Yang @ 2025-07-04 8:15 UTC (permalink / raw)
To: Andy Shevchenko
Cc: ezequiel, mchehab, laurent.pinchart, hdegoede, gregkh, mingo,
tglx, viro, thomas.weissschuh, linux-media, linux-kernel,
linux-usb, imx, jun.li
On Thu, Jul 03, 2025 at 04:46:57PM +0300, Andy Shevchenko wrote:
> On Thu, Jul 03, 2025 at 06:38:09PM +0800, Xu Yang wrote:
> > This will add usb_alloc_noncoherent() and usb_free_noncoherent()
> > functions to support alloc and free buffer in a dma-noncoherent way.
> >
> > To explicit manage the memory ownership for the kernel and device,
> > this will also add usb_dma_noncoherent_sync_for_cpu/device() functions
> > and call it at proper time. The management requires the user save
> > sg_table returned by usb_alloc_noncoherent() to urb->sgt.
>
> ...
>
> > dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> > - if (urb->transfer_buffer_length != 0
> > - && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
> > + if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
> > + if (!urb->transfer_buffer_length)
> > + return ret;
>
> This return ret out of a sudden are quite fragile. Please, move to use return 0
> directly where it is supposed to be 0.
Okay.
>
> ...
>
> > + } else {
> > + if (!urb->sgt)
> > + return ret;
>
> Ditto.
Okay.
>
> > + if (dir == DMA_TO_DEVICE)
> > + flush_kernel_vmap_range(urb->transfer_buffer,
> > + urb->transfer_buffer_length);
> > + dma_sync_sgtable_for_device(hcd->self.sysdev, urb->sgt, dir);
> > }
>
> > return ret;
>
> Ditto?
With Alan's suggestion, this patch will not touch this line.
Thanks,
Xu Yang
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/3] usb: core: add dma-noncoherent buffer alloc and free API
2025-07-03 10:38 ` [PATCH v4 1/3] usb: core: add dma-noncoherent buffer alloc and free API Xu Yang
2025-07-03 13:46 ` Andy Shevchenko
@ 2025-07-03 14:35 ` Alan Stern
2025-07-04 8:16 ` Xu Yang
1 sibling, 1 reply; 9+ messages in thread
From: Alan Stern @ 2025-07-03 14:35 UTC (permalink / raw)
To: Xu Yang
Cc: ezequiel, mchehab, laurent.pinchart, hdegoede, gregkh, mingo,
tglx, andriy.shevchenko, viro, thomas.weissschuh, linux-media,
linux-kernel, linux-usb, imx, jun.li
On Thu, Jul 03, 2025 at 06:38:09PM +0800, Xu Yang wrote:
> This will add usb_alloc_noncoherent() and usb_free_noncoherent()
> functions to support alloc and free buffer in a dma-noncoherent way.
>
> To explicit manage the memory ownership for the kernel and device,
> this will also add usb_dma_noncoherent_sync_for_cpu/device() functions
> and call it at proper time. The management requires the user save
> sg_table returned by usb_alloc_noncoherent() to urb->sgt.
>
> Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
>
> ---
> Changes in v4:
> - improve if-else logic
>
> Changes in v3:
> - put Return section at the end of description
> - correct some abbreviations
> - remove usb_dma_noncoherent_sync_for_cpu() and
> usb_dma_noncoherent_sync_for_device()
> - do DMA sync in usb_hcd_map_urb_for_dma() and
> usb_hcd_unmap_urb_for_dma()
> - call flush_kernel_vmap_range() for OUT transfers
> and invalidate_kernel_vmap_range() for IN transfers
> ---
> drivers/usb/core/hcd.c | 33 ++++++++++++-----
> drivers/usb/core/usb.c | 80 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/usb.h | 11 ++++++
> 3 files changed, 116 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> index c22de97432a0..42d9d8db0968 100644
> --- a/drivers/usb/core/hcd.c
> +++ b/drivers/usb/core/hcd.c
> @@ -1425,8 +1431,10 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
> }
>
> dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> - if (urb->transfer_buffer_length != 0
> - && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
> + if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
> + if (!urb->transfer_buffer_length)
> + return ret;
> +
> if (hcd->localmem_pool) {
> ret = hcd_alloc_coherent(
> urb->dev->bus, mem_flags,
> @@ -1491,7 +1499,16 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
> if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE |
> URB_SETUP_MAP_LOCAL)))
> usb_hcd_unmap_urb_for_dma(hcd, urb);
> + } else {
> + if (!urb->sgt)
> + return ret;
> +
> + if (dir == DMA_TO_DEVICE)
> + flush_kernel_vmap_range(urb->transfer_buffer,
> + urb->transfer_buffer_length);
> + dma_sync_sgtable_for_device(hcd->self.sysdev, urb->sgt, dir);
> }
This could be done a little more cleanly. It's always awkward to read
an "else" clause for a negated test.
Instead, change the "else" to:
if (urb->transfer_flags & URB_NO_TRANFER_DMA_MAP) {
and move this whole section to the top of the big "if". Then you can
change the test that's already there to:
} else if (urb->transfer_buffer_length != 0) {
Alan Stern
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v4 1/3] usb: core: add dma-noncoherent buffer alloc and free API
2025-07-03 14:35 ` Alan Stern
@ 2025-07-04 8:16 ` Xu Yang
0 siblings, 0 replies; 9+ messages in thread
From: Xu Yang @ 2025-07-04 8:16 UTC (permalink / raw)
To: Alan Stern
Cc: ezequiel, mchehab, laurent.pinchart, hdegoede, gregkh, mingo,
tglx, andriy.shevchenko, viro, thomas.weissschuh, linux-media,
linux-kernel, linux-usb, imx, jun.li
On Thu, Jul 03, 2025 at 10:35:23AM -0400, Alan Stern wrote:
> On Thu, Jul 03, 2025 at 06:38:09PM +0800, Xu Yang wrote:
> > This will add usb_alloc_noncoherent() and usb_free_noncoherent()
> > functions to support alloc and free buffer in a dma-noncoherent way.
> >
> > To explicit manage the memory ownership for the kernel and device,
> > this will also add usb_dma_noncoherent_sync_for_cpu/device() functions
> > and call it at proper time. The management requires the user save
> > sg_table returned by usb_alloc_noncoherent() to urb->sgt.
> >
> > Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
> >
> > ---
> > Changes in v4:
> > - improve if-else logic
> >
> > Changes in v3:
> > - put Return section at the end of description
> > - correct some abbreviations
> > - remove usb_dma_noncoherent_sync_for_cpu() and
> > usb_dma_noncoherent_sync_for_device()
> > - do DMA sync in usb_hcd_map_urb_for_dma() and
> > usb_hcd_unmap_urb_for_dma()
> > - call flush_kernel_vmap_range() for OUT transfers
> > and invalidate_kernel_vmap_range() for IN transfers
> > ---
> > drivers/usb/core/hcd.c | 33 ++++++++++++-----
> > drivers/usb/core/usb.c | 80 ++++++++++++++++++++++++++++++++++++++++++
> > include/linux/usb.h | 11 ++++++
> > 3 files changed, 116 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
> > index c22de97432a0..42d9d8db0968 100644
> > --- a/drivers/usb/core/hcd.c
> > +++ b/drivers/usb/core/hcd.c
>
> > @@ -1425,8 +1431,10 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
> > }
> >
> > dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> > - if (urb->transfer_buffer_length != 0
> > - && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
> > + if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
> > + if (!urb->transfer_buffer_length)
> > + return ret;
> > +
> > if (hcd->localmem_pool) {
> > ret = hcd_alloc_coherent(
> > urb->dev->bus, mem_flags,
> > @@ -1491,7 +1499,16 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
> > if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE |
> > URB_SETUP_MAP_LOCAL)))
> > usb_hcd_unmap_urb_for_dma(hcd, urb);
> > + } else {
> > + if (!urb->sgt)
> > + return ret;
> > +
> > + if (dir == DMA_TO_DEVICE)
> > + flush_kernel_vmap_range(urb->transfer_buffer,
> > + urb->transfer_buffer_length);
> > + dma_sync_sgtable_for_device(hcd->self.sysdev, urb->sgt, dir);
> > }
>
> This could be done a little more cleanly. It's always awkward to read
> an "else" clause for a negated test.
Agree.
>
> Instead, change the "else" to:
>
> if (urb->transfer_flags & URB_NO_TRANFER_DMA_MAP) {
>
> and move this whole section to the top of the big "if". Then you can
> change the test that's already there to:
>
> } else if (urb->transfer_buffer_length != 0) {
Okay. It's a better choice.
Thanks,
Xu Yang
>
> Alan Stern
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 2/3] media: uvcvideo: use usb_alloc_noncoherent/usb_free_noncoherent()
2025-07-03 10:38 [PATCH v4 0/3] add dma noncoherent API Xu Yang
2025-07-03 10:38 ` [PATCH v4 1/3] usb: core: add dma-noncoherent buffer alloc and free API Xu Yang
@ 2025-07-03 10:38 ` Xu Yang
2025-07-03 11:34 ` Ricardo Ribalda
2025-07-03 10:38 ` [PATCH v4 3/3] media: stk1160: " Xu Yang
2 siblings, 1 reply; 9+ messages in thread
From: Xu Yang @ 2025-07-03 10:38 UTC (permalink / raw)
To: ezequiel, mchehab, laurent.pinchart, hdegoede, gregkh, xu.yang_2,
mingo, tglx, andriy.shevchenko, viro, thomas.weissschuh
Cc: linux-media, linux-kernel, linux-usb, imx, jun.li
This will use USB noncoherent API to alloc/free urb buffers, then
uvc driver needn't to do dma sync operations by itself.
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
Changes in v4:
- remove uvc_stream_to_dmadev()
Changes in v3:
- no changes
---
drivers/media/usb/uvc/uvc_video.c | 61 +++++++------------------------
1 file changed, 14 insertions(+), 47 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index e3567aeb0007..a75af314e46b 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1275,20 +1275,6 @@ static inline enum dma_data_direction uvc_stream_dir(
return DMA_TO_DEVICE;
}
-static inline struct device *uvc_stream_to_dmadev(struct uvc_streaming *stream)
-{
- return bus_to_hcd(stream->dev->udev->bus)->self.sysdev;
-}
-
-static int uvc_submit_urb(struct uvc_urb *uvc_urb, gfp_t mem_flags)
-{
- /* Sync DMA. */
- dma_sync_sgtable_for_device(uvc_stream_to_dmadev(uvc_urb->stream),
- uvc_urb->sgt,
- uvc_stream_dir(uvc_urb->stream));
- return usb_submit_urb(uvc_urb->urb, mem_flags);
-}
-
/*
* uvc_video_decode_data_work: Asynchronous memcpy processing
*
@@ -1310,7 +1296,7 @@ static void uvc_video_copy_data_work(struct work_struct *work)
uvc_queue_buffer_release(op->buf);
}
- ret = uvc_submit_urb(uvc_urb, GFP_KERNEL);
+ ret = usb_submit_urb(uvc_urb->urb, GFP_KERNEL);
if (ret < 0)
dev_err(&uvc_urb->stream->intf->dev,
"Failed to resubmit video URB (%d).\n", ret);
@@ -1736,12 +1722,6 @@ static void uvc_video_complete(struct urb *urb)
/* Re-initialise the URB async work. */
uvc_urb->async_operations = 0;
- /* Sync DMA and invalidate vmap range. */
- dma_sync_sgtable_for_cpu(uvc_stream_to_dmadev(uvc_urb->stream),
- uvc_urb->sgt, uvc_stream_dir(stream));
- invalidate_kernel_vmap_range(uvc_urb->buffer,
- uvc_urb->stream->urb_size);
-
/*
* Process the URB headers, and optionally queue expensive memcpy tasks
* to be deferred to a work queue.
@@ -1750,7 +1730,7 @@ static void uvc_video_complete(struct urb *urb)
/* If no async work is needed, resubmit the URB immediately. */
if (!uvc_urb->async_operations) {
- ret = uvc_submit_urb(uvc_urb, GFP_ATOMIC);
+ ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC);
if (ret < 0)
dev_err(&stream->intf->dev,
"Failed to resubmit video URB (%d).\n", ret);
@@ -1765,17 +1745,15 @@ static void uvc_video_complete(struct urb *urb)
*/
static void uvc_free_urb_buffers(struct uvc_streaming *stream)
{
- struct device *dma_dev = uvc_stream_to_dmadev(stream);
+ struct usb_device *udev = stream->dev->udev;
struct uvc_urb *uvc_urb;
for_each_uvc_urb(uvc_urb, stream) {
if (!uvc_urb->buffer)
continue;
- dma_vunmap_noncontiguous(dma_dev, uvc_urb->buffer);
- dma_free_noncontiguous(dma_dev, stream->urb_size, uvc_urb->sgt,
- uvc_stream_dir(stream));
-
+ usb_free_noncoherent(udev, stream->urb_size, uvc_urb->buffer,
+ uvc_stream_dir(stream), uvc_urb->sgt);
uvc_urb->buffer = NULL;
uvc_urb->sgt = NULL;
}
@@ -1786,26 +1764,13 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
static bool uvc_alloc_urb_buffer(struct uvc_streaming *stream,
struct uvc_urb *uvc_urb, gfp_t gfp_flags)
{
- struct device *dma_dev = uvc_stream_to_dmadev(stream);
-
- uvc_urb->sgt = dma_alloc_noncontiguous(dma_dev, stream->urb_size,
- uvc_stream_dir(stream),
- gfp_flags, 0);
- if (!uvc_urb->sgt)
- return false;
- uvc_urb->dma = uvc_urb->sgt->sgl->dma_address;
-
- uvc_urb->buffer = dma_vmap_noncontiguous(dma_dev, stream->urb_size,
- uvc_urb->sgt);
- if (!uvc_urb->buffer) {
- dma_free_noncontiguous(dma_dev, stream->urb_size,
- uvc_urb->sgt,
- uvc_stream_dir(stream));
- uvc_urb->sgt = NULL;
- return false;
- }
+ struct usb_device *udev = stream->dev->udev;
- return true;
+ uvc_urb->buffer = usb_alloc_noncoherent(udev, stream->urb_size,
+ gfp_flags, &uvc_urb->dma,
+ uvc_stream_dir(stream),
+ &uvc_urb->sgt);
+ return !!uvc_urb->buffer;
}
/*
@@ -1953,6 +1918,7 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
urb->complete = uvc_video_complete;
urb->number_of_packets = npackets;
urb->transfer_buffer_length = size;
+ urb->sgt = uvc_urb->sgt;
for (i = 0; i < npackets; ++i) {
urb->iso_frame_desc[i].offset = i * psize;
@@ -2009,6 +1975,7 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
size, uvc_video_complete, uvc_urb);
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = uvc_urb->dma;
+ urb->sgt = uvc_urb->sgt;
uvc_urb->urb = urb;
}
@@ -2120,7 +2087,7 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream,
/* Submit the URBs. */
for_each_uvc_urb(uvc_urb, stream) {
- ret = uvc_submit_urb(uvc_urb, gfp_flags);
+ ret = usb_submit_urb(uvc_urb->urb, gfp_flags);
if (ret < 0) {
dev_err(&stream->intf->dev,
"Failed to submit URB %u (%d).\n",
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v4 2/3] media: uvcvideo: use usb_alloc_noncoherent/usb_free_noncoherent()
2025-07-03 10:38 ` [PATCH v4 2/3] media: uvcvideo: use usb_alloc_noncoherent/usb_free_noncoherent() Xu Yang
@ 2025-07-03 11:34 ` Ricardo Ribalda
0 siblings, 0 replies; 9+ messages in thread
From: Ricardo Ribalda @ 2025-07-03 11:34 UTC (permalink / raw)
To: Xu Yang
Cc: ezequiel, mchehab, laurent.pinchart, hdegoede, gregkh, mingo,
tglx, andriy.shevchenko, viro, thomas.weissschuh, linux-media,
linux-kernel, linux-usb, imx, jun.li
On Thu, 3 Jul 2025 at 13:02, Xu Yang <xu.yang_2@nxp.com> wrote:
>
> This will use USB noncoherent API to alloc/free urb buffers, then
> uvc driver needn't to do dma sync operations by itself.
>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
>
> ---
> Changes in v4:
> - remove uvc_stream_to_dmadev()
> Changes in v3:
> - no changes
> ---
> drivers/media/usb/uvc/uvc_video.c | 61 +++++++------------------------
> 1 file changed, 14 insertions(+), 47 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
> index e3567aeb0007..a75af314e46b 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -1275,20 +1275,6 @@ static inline enum dma_data_direction uvc_stream_dir(
> return DMA_TO_DEVICE;
> }
>
> -static inline struct device *uvc_stream_to_dmadev(struct uvc_streaming *stream)
> -{
> - return bus_to_hcd(stream->dev->udev->bus)->self.sysdev;
> -}
> -
> -static int uvc_submit_urb(struct uvc_urb *uvc_urb, gfp_t mem_flags)
> -{
> - /* Sync DMA. */
> - dma_sync_sgtable_for_device(uvc_stream_to_dmadev(uvc_urb->stream),
> - uvc_urb->sgt,
> - uvc_stream_dir(uvc_urb->stream));
> - return usb_submit_urb(uvc_urb->urb, mem_flags);
> -}
> -
> /*
> * uvc_video_decode_data_work: Asynchronous memcpy processing
> *
> @@ -1310,7 +1296,7 @@ static void uvc_video_copy_data_work(struct work_struct *work)
> uvc_queue_buffer_release(op->buf);
> }
>
> - ret = uvc_submit_urb(uvc_urb, GFP_KERNEL);
> + ret = usb_submit_urb(uvc_urb->urb, GFP_KERNEL);
> if (ret < 0)
> dev_err(&uvc_urb->stream->intf->dev,
> "Failed to resubmit video URB (%d).\n", ret);
> @@ -1736,12 +1722,6 @@ static void uvc_video_complete(struct urb *urb)
> /* Re-initialise the URB async work. */
> uvc_urb->async_operations = 0;
>
> - /* Sync DMA and invalidate vmap range. */
> - dma_sync_sgtable_for_cpu(uvc_stream_to_dmadev(uvc_urb->stream),
> - uvc_urb->sgt, uvc_stream_dir(stream));
> - invalidate_kernel_vmap_range(uvc_urb->buffer,
> - uvc_urb->stream->urb_size);
> -
> /*
> * Process the URB headers, and optionally queue expensive memcpy tasks
> * to be deferred to a work queue.
> @@ -1750,7 +1730,7 @@ static void uvc_video_complete(struct urb *urb)
>
> /* If no async work is needed, resubmit the URB immediately. */
> if (!uvc_urb->async_operations) {
> - ret = uvc_submit_urb(uvc_urb, GFP_ATOMIC);
> + ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC);
> if (ret < 0)
> dev_err(&stream->intf->dev,
> "Failed to resubmit video URB (%d).\n", ret);
> @@ -1765,17 +1745,15 @@ static void uvc_video_complete(struct urb *urb)
> */
> static void uvc_free_urb_buffers(struct uvc_streaming *stream)
> {
> - struct device *dma_dev = uvc_stream_to_dmadev(stream);
> + struct usb_device *udev = stream->dev->udev;
> struct uvc_urb *uvc_urb;
>
> for_each_uvc_urb(uvc_urb, stream) {
> if (!uvc_urb->buffer)
> continue;
>
> - dma_vunmap_noncontiguous(dma_dev, uvc_urb->buffer);
> - dma_free_noncontiguous(dma_dev, stream->urb_size, uvc_urb->sgt,
> - uvc_stream_dir(stream));
> -
> + usb_free_noncoherent(udev, stream->urb_size, uvc_urb->buffer,
> + uvc_stream_dir(stream), uvc_urb->sgt);
> uvc_urb->buffer = NULL;
> uvc_urb->sgt = NULL;
> }
> @@ -1786,26 +1764,13 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
> static bool uvc_alloc_urb_buffer(struct uvc_streaming *stream,
> struct uvc_urb *uvc_urb, gfp_t gfp_flags)
> {
> - struct device *dma_dev = uvc_stream_to_dmadev(stream);
> -
> - uvc_urb->sgt = dma_alloc_noncontiguous(dma_dev, stream->urb_size,
> - uvc_stream_dir(stream),
> - gfp_flags, 0);
> - if (!uvc_urb->sgt)
> - return false;
> - uvc_urb->dma = uvc_urb->sgt->sgl->dma_address;
> -
> - uvc_urb->buffer = dma_vmap_noncontiguous(dma_dev, stream->urb_size,
> - uvc_urb->sgt);
> - if (!uvc_urb->buffer) {
> - dma_free_noncontiguous(dma_dev, stream->urb_size,
> - uvc_urb->sgt,
> - uvc_stream_dir(stream));
> - uvc_urb->sgt = NULL;
> - return false;
> - }
> + struct usb_device *udev = stream->dev->udev;
>
> - return true;
> + uvc_urb->buffer = usb_alloc_noncoherent(udev, stream->urb_size,
> + gfp_flags, &uvc_urb->dma,
> + uvc_stream_dir(stream),
> + &uvc_urb->sgt);
> + return !!uvc_urb->buffer;
> }
>
> /*
> @@ -1953,6 +1918,7 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
> urb->complete = uvc_video_complete;
> urb->number_of_packets = npackets;
> urb->transfer_buffer_length = size;
> + urb->sgt = uvc_urb->sgt;
>
> for (i = 0; i < npackets; ++i) {
> urb->iso_frame_desc[i].offset = i * psize;
> @@ -2009,6 +1975,7 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
> size, uvc_video_complete, uvc_urb);
> urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
> urb->transfer_dma = uvc_urb->dma;
> + urb->sgt = uvc_urb->sgt;
>
> uvc_urb->urb = urb;
> }
> @@ -2120,7 +2087,7 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream,
>
> /* Submit the URBs. */
> for_each_uvc_urb(uvc_urb, stream) {
> - ret = uvc_submit_urb(uvc_urb, gfp_flags);
> + ret = usb_submit_urb(uvc_urb->urb, gfp_flags);
> if (ret < 0) {
> dev_err(&stream->intf->dev,
> "Failed to submit URB %u (%d).\n",
> --
> 2.34.1
>
>
--
Ricardo Ribalda
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 3/3] media: stk1160: use usb_alloc_noncoherent/usb_free_noncoherent()
2025-07-03 10:38 [PATCH v4 0/3] add dma noncoherent API Xu Yang
2025-07-03 10:38 ` [PATCH v4 1/3] usb: core: add dma-noncoherent buffer alloc and free API Xu Yang
2025-07-03 10:38 ` [PATCH v4 2/3] media: uvcvideo: use usb_alloc_noncoherent/usb_free_noncoherent() Xu Yang
@ 2025-07-03 10:38 ` Xu Yang
2 siblings, 0 replies; 9+ messages in thread
From: Xu Yang @ 2025-07-03 10:38 UTC (permalink / raw)
To: ezequiel, mchehab, laurent.pinchart, hdegoede, gregkh, xu.yang_2,
mingo, tglx, andriy.shevchenko, viro, thomas.weissschuh
Cc: linux-media, linux-kernel, linux-usb, imx, jun.li
This will use USB noncoherent API to alloc/free urb buffers, then
stk1160 driver needn't to do dma sync operations by itself.
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
Changes in v4:
- no changes
Changes in v3:
- no changes
---
drivers/media/usb/stk1160/stk1160-v4l.c | 4 ---
drivers/media/usb/stk1160/stk1160-video.c | 43 ++++++-----------------
drivers/media/usb/stk1160/stk1160.h | 7 ----
3 files changed, 11 insertions(+), 43 deletions(-)
diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c
index 5ba3d9c4b3fb..715ce1dcb304 100644
--- a/drivers/media/usb/stk1160/stk1160-v4l.c
+++ b/drivers/media/usb/stk1160/stk1160-v4l.c
@@ -232,10 +232,6 @@ static int stk1160_start_streaming(struct stk1160 *dev)
/* submit urbs and enables IRQ */
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- struct stk1160_urb *stk_urb = &dev->isoc_ctl.urb_ctl[i];
-
- dma_sync_sgtable_for_device(stk1160_get_dmadev(dev), stk_urb->sgt,
- DMA_FROM_DEVICE);
rc = usb_submit_urb(dev->isoc_ctl.urb_ctl[i].urb, GFP_KERNEL);
if (rc) {
stk1160_err("cannot submit urb[%d] (%d)\n", i, rc);
diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c
index 9cbd957ecc90..416cb74377eb 100644
--- a/drivers/media/usb/stk1160/stk1160-video.c
+++ b/drivers/media/usb/stk1160/stk1160-video.c
@@ -298,9 +298,7 @@ static void stk1160_process_isoc(struct stk1160 *dev, struct urb *urb)
static void stk1160_isoc_irq(struct urb *urb)
{
int i, rc;
- struct stk1160_urb *stk_urb = urb->context;
- struct stk1160 *dev = stk_urb->dev;
- struct device *dma_dev = stk1160_get_dmadev(dev);
+ struct stk1160 *dev = urb->context;
switch (urb->status) {
case 0:
@@ -315,10 +313,6 @@ static void stk1160_isoc_irq(struct urb *urb)
return;
}
- invalidate_kernel_vmap_range(stk_urb->transfer_buffer,
- urb->transfer_buffer_length);
- dma_sync_sgtable_for_cpu(dma_dev, stk_urb->sgt, DMA_FROM_DEVICE);
-
stk1160_process_isoc(dev, urb);
/* Reset urb buffers */
@@ -327,7 +321,6 @@ static void stk1160_isoc_irq(struct urb *urb)
urb->iso_frame_desc[i].actual_length = 0;
}
- dma_sync_sgtable_for_device(dma_dev, stk_urb->sgt, DMA_FROM_DEVICE);
rc = usb_submit_urb(urb, GFP_ATOMIC);
if (rc)
stk1160_err("urb re-submit failed (%d)\n", rc);
@@ -365,11 +358,9 @@ void stk1160_cancel_isoc(struct stk1160 *dev)
static void stk_free_urb(struct stk1160 *dev, struct stk1160_urb *stk_urb)
{
- struct device *dma_dev = stk1160_get_dmadev(dev);
-
- dma_vunmap_noncontiguous(dma_dev, stk_urb->transfer_buffer);
- dma_free_noncontiguous(dma_dev, stk_urb->urb->transfer_buffer_length,
- stk_urb->sgt, DMA_FROM_DEVICE);
+ usb_free_noncoherent(dev->udev, stk_urb->urb->transfer_buffer_length,
+ stk_urb->transfer_buffer, DMA_FROM_DEVICE,
+ stk_urb->sgt);
usb_free_urb(stk_urb->urb);
stk_urb->transfer_buffer = NULL;
@@ -410,32 +401,19 @@ void stk1160_uninit_isoc(struct stk1160 *dev)
static int stk1160_fill_urb(struct stk1160 *dev, struct stk1160_urb *stk_urb,
int sb_size, int max_packets)
{
- struct device *dma_dev = stk1160_get_dmadev(dev);
-
stk_urb->urb = usb_alloc_urb(max_packets, GFP_KERNEL);
if (!stk_urb->urb)
return -ENOMEM;
- stk_urb->sgt = dma_alloc_noncontiguous(dma_dev, sb_size,
- DMA_FROM_DEVICE, GFP_KERNEL, 0);
-
- /*
- * If the buffer allocation failed, we exit but return 0 since
- * we allow the driver working with less buffers
- */
- if (!stk_urb->sgt)
- goto free_urb;
- stk_urb->transfer_buffer = dma_vmap_noncontiguous(dma_dev, sb_size,
- stk_urb->sgt);
+ stk_urb->transfer_buffer = usb_alloc_noncoherent(dev->udev, sb_size,
+ GFP_KERNEL, &stk_urb->dma,
+ DMA_FROM_DEVICE, &stk_urb->sgt);
if (!stk_urb->transfer_buffer)
- goto free_sgt;
+ goto free_urb;
- stk_urb->dma = stk_urb->sgt->sgl->dma_address;
stk_urb->dev = dev;
return 0;
-free_sgt:
- dma_free_noncontiguous(dma_dev, sb_size, stk_urb->sgt, DMA_FROM_DEVICE);
- stk_urb->sgt = NULL;
+
free_urb:
usb_free_urb(stk_urb->urb);
stk_urb->urb = NULL;
@@ -494,12 +472,13 @@ int stk1160_alloc_isoc(struct stk1160 *dev)
urb->transfer_buffer = dev->isoc_ctl.urb_ctl[i].transfer_buffer;
urb->transfer_buffer_length = sb_size;
urb->complete = stk1160_isoc_irq;
- urb->context = &dev->isoc_ctl.urb_ctl[i];
+ urb->context = dev;
urb->interval = 1;
urb->start_frame = 0;
urb->number_of_packets = max_packets;
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = dev->isoc_ctl.urb_ctl[i].dma;
+ urb->sgt = dev->isoc_ctl.urb_ctl[i].sgt;
k = 0;
for (j = 0; j < max_packets; j++) {
diff --git a/drivers/media/usb/stk1160/stk1160.h b/drivers/media/usb/stk1160/stk1160.h
index 7b498d14ed7a..4cbcb0a03bab 100644
--- a/drivers/media/usb/stk1160/stk1160.h
+++ b/drivers/media/usb/stk1160/stk1160.h
@@ -16,8 +16,6 @@
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <linux/usb.h>
-#include <linux/usb/hcd.h>
#define STK1160_VERSION "0.9.5"
#define STK1160_VERSION_NUM 0x000905
@@ -195,8 +193,3 @@ void stk1160_select_input(struct stk1160 *dev);
/* Provided by stk1160-ac97.c */
void stk1160_ac97_setup(struct stk1160 *dev);
-
-static inline struct device *stk1160_get_dmadev(struct stk1160 *dev)
-{
- return bus_to_hcd(dev->udev->bus)->self.sysdev;
-}
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread