vb2: Handle user cache hints, allow drivers to choose cache coherency
Subsystems (4)
cover vb2: Handle user cache hints, allow drivers to choose cache coherency
Hi folks,
This RFC patchset achieves two main objectives:
1. Respects cache flags passed from the user space. As no driver nor videobuf2 has (ever?) implemented them, the two flags are replaced by a single one (V4L2_BUF_FLAG_NO_CACHE_SYNC) and the two old flags are deprecated. This is done since a single flag provides the driver with enough information on what to do. (See more info in patch 4.)
2. Allows a driver using videobuf2 dma-contig memory type to choose whether it prefers coherent or non-coherent CPU access to buffer memory for MMAP and USERPTR buffers. This could be later extended to be specified by the user, and per buffer if needed.
Only dma-contig memory type is changed but the same could be done to dma-sg as well. I can add it to the set if people are happy with the changes to dma-contig.
Signature · 2 lines
Kind regards, Sakari
RFC 1/11 vb2: Rename confusingly named internal buffer preparation functions
Rename __qbuf_*() functions which are specific to a buffer type as __prepare_*() which matches with what they do. The naming was there for historical reasons; the purpose of the functions was changed without renaming them.
--- drivers/media/v4l2-core/videobuf2-core.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-)
drivers/media/v4l2-core/videobuf2-core.c +10 -9
@@ -1391,18 +1391,19 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b }/** -* __qbuf_mmap() - handle qbuf of an MMAP buffer +* __prepare_mmap() - prepare an MMAP buffer */ -static int __qbuf_mmap (struct vb2_buffer *vb,const struct v4l2_buffer *b) +static int __prepare_mmap (struct vb2_buffer *vb,const struct v4l2_buffer *b) {__fill_vb2_buffer (vb, b, vb->v4l2_planes);return call_vb_qop (vb, buf_prepare, vb); }/** -* __qbuf_userptr() - handle qbuf of a USERPTR buffer +* __prepare_userptr() - prepare a USERPTR buffer */ -static int __qbuf_userptr (struct vb2_buffer *vb,const struct v4l2_buffer *b) +static int __prepare_userptr (struct vb2_buffer *vb, +const struct v4l2_buffer *b) {struct v4l2_plane planes[VIDEO_MAX_PLANES ];struct vb2_queue *q = vb->vb2_queue;
@@ -1504,9 +1505,9 @@ err: }/** -* __qbuf_dmabuf() - handle qbuf of a DMABUF buffer +* __prepare_dmabuf() - prepare a DMABUF buffer */ -static int __qbuf_dmabuf (struct vb2_buffer *vb,const struct v4l2_buffer *b) +static int __prepare_dmabuf (struct vb2_buffer *vb,const struct v4l2_buffer *b) {struct v4l2_plane planes[VIDEO_MAX_PLANES ];struct vb2_queue *q = vb->vb2_queue;
@@ -1678,15 +1679,15 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)switch (q->memory) {case V4L2_MEMORY_MMAP : - ret =__qbuf_mmap (vb, b); + ret =__prepare_mmap (vb, b);break ;case V4L2_MEMORY_USERPTR :down_read (¤t->mm->mmap_sem); - ret =__qbuf_userptr (vb, b); + ret =__prepare_userptr (vb, b);up_read (¤t->mm->mmap_sem);break ;case V4L2_MEMORY_DMABUF : - ret =__qbuf_dmabuf (vb, b); + ret =__prepare_dmabuf (vb, b);break ;default :WARN (1 ,"Invalid queue type\n" );
Signature · 1 line
2.1.0.231.g7484e3b
RFC 2/11 vb2: Move buffer cache synchronisation to prepare from queue
In reply to Sakari AilusThe buffer cache should be synchronised in buffer preparation, not when the buffer is queued to the device. Fix this.
Mmap buffers do not need cache synchronisation since they are always coherent.
--- drivers/media/v4l2-core/videobuf2-core.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
drivers/media/v4l2-core/videobuf2-core.c +11 -7
@@ -1636,10 +1636,6 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)trace_vb2_buf_queue (q, vb); -/* sync buffers */ -for (plane =0 ; plane < vb->num_planes; ++plane) -call_void_memop (vb, prepare, vb->planes[plane].mem_priv); -call_void_vb_qop (vb, buf_queue, vb); }
@@ -1694,11 +1690,19 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ret = -EINVAL ; } -if (ret) +if (ret) {dprintk (1 ,"buffer preparation failed: %d\n" , ret); - vb->state = ret ?VB2_BUF_STATE_DEQUEUED :VB2_BUF_STATE_PREPARED ; + vb->state =VB2_BUF_STATE_DEQUEUED ; +return ret; + } -return ret; +/* sync buffers */ +for (plane =0 ; plane < vb->num_planes; ++plane) +call_void_memop (vb, prepare, vb->planes[plane].mem_priv); + + vb->state =VB2_BUF_STATE_PREPARED ; + +return 0 ; } static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
Signature · 1 line
2.1.0.231.g7484e3b
RFC 3/11 vb2: Move cache synchronisation from buffer done to dqbuf handler
In reply to Sakari AilusThe cache synchronisation may be a time consuming operation and thus not best performed in an interrupt which is a typical context for vb2_buffer_done() calls. This may consume up to tens of ms on some machines, depending on the buffer size.
--- drivers/media/v4l2-core/videobuf2-core.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
drivers/media/v4l2-core/videobuf2-core.c +10 -10
@@ -1177,7 +1177,6 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) {struct vb2_queue *q = vb->vb2_queue;unsigned long flags; -unsigned int plane;if (WARN_ON (vb->state !=VB2_BUF_STATE_ACTIVE ))return ;
@@ -1197,10 +1196,6 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)dprintk (4 ,"done processing on buffer %d, state: %d\n" , vb->v4l2_buf.index, state); -/* sync buffers */ -for (plane =0 ; plane < vb->num_planes; ++plane) -call_void_memop (vb, finish, vb->planes[plane].mem_priv); -/* Add the buffer to the done buffers list */ spin_lock_irqsave (&q->done_lock, flags); vb->state = state;
@@ -2086,7 +2081,7 @@ EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);static void __vb2_dqbuf (struct vb2_buffer *vb) {struct vb2_queue *q = vb->vb2_queue; -unsigned int i; +unsigned int plane;/* nothing to do if the buffer is already dequeued */ if (vb->state ==VB2_BUF_STATE_DEQUEUED )
@@ -2094,13 +2089,18 @@ static void __vb2_dqbuf(struct vb2_buffer *vb) vb->state =VB2_BUF_STATE_DEQUEUED ; +/* sync buffers */ +for (plane =0 ; plane < vb->num_planes; plane++) +call_void_memop (vb, finish, vb->planes[plane].mem_priv); +/* unmap DMABUF buffer */ if (q->memory ==V4L2_MEMORY_DMABUF ) -for (i =0 ; i < vb->num_planes; ++i) { -if (!vb->planes[i].dbuf_mapped) +for (plane =0 ; plane < vb->num_planes; ++plane) { +if (!vb->planes[plane].dbuf_mapped)continue ; -call_void_memop (vb, unmap_dmabuf, vb->planes[i].mem_priv); - vb->planes[i].dbuf_mapped =0 ; +call_void_memop (vb, unmap_dmabuf, + vb->planes[plane].mem_priv); + vb->planes[plane].dbuf_mapped =0 ; } }
Signature · 1 line
2.1.0.231.g7484e3b
RFC 4/11 v4l: Unify cache management hint buffer flags
In reply to Sakari AilusThe V4L2_BUF_FLAG_NO_CACHE_INVALIDATE and V4L2_BUF_FLAG_NO_CACHE_CLEAN buffer flags are currently not used by the kernel. Replace the definitions by a single V4L2_BUF_FLAG_NO_CACHE_SYNC flag to be used by further patches.
Different cache architectures should not be visible to the user space which can make no meaningful use of the differences anyway. In case a device can make use of non-coherent memory accesses, the necessary cache operations depend on the CPU architecture and the buffer type, not the requests of the user. The cache operation itself may be skipped on the user's request which was the purpose of the two flags.
On ARM the invalidate and clean are separate operations whereas on x86(-64) the two are a single operation (flush). Whether the hardware uses the buffer for reading (V4L2_BUF_TYPE_*_OUTPUT*) or writing (V4L2_BUF_TYPE_*CAPTURE*) already defines the required cache operation (clean and invalidate, respectively). No user input is required.
--- Documentation/DocBook/media/v4l/io.xml | 25 +++++++++++-------------- include/trace/events/v4l2.h | 3 +-- include/uapi/linux/videodev2.h | 7 +++++-- 3 files changed, 17 insertions(+), 18 deletions(-)
Documentation/DocBook/media/v4l/io.xml +11 -14
@@ -1112,21 +1112,18 @@ application. Drivers set or clear this flag when the linkend="vidioc-qbuf">VIDIOC_DQBUF</link> ioctl is called.</entry> </row> <row> - <entry><constant>V4L2_BUF_FLAG_NO_CACHE_INVALIDATE</constant></entry> + <entry><constant>V4L2_BUF_FLAG_NO_CACHE_SYNC</constant></entry> <entry>0x00000800</entry> - <entry>Caches do not have to be invalidated for this buffer. -Typically applications shall use this flag if the data captured in the buffer -is not going to be touched by the CPU, instead the buffer will, probably, be -passed on to a DMA-capable hardware unit for further processing or output. -</entry> - </row> - <row> - <entry><constant>V4L2_BUF_FLAG_NO_CACHE_CLEAN</constant></entry> - <entry>0x00001000</entry> - <entry>Caches do not have to be cleaned for this buffer. -Typically applications shall use this flag for output buffers if the data -in this buffer has not been created by the CPU but by some DMA-capable unit, -in which case caches have not been used.</entry> + <entry>Do not perform CPU cache synchronisation operations + when the buffer is queued or dequeued. The user is + responsible for the correct use of this flag. It should be + only used when the buffer is not accessed using the CPU, + e.g. the buffer is written to by a hardware block and then + read by another one, in which case the flag should be set + in both <link linkend="vidioc-qbuf">VIDIOC_DQBUF</link> + and <link linkend="vidioc-qbuf">VIDIOC_QBUF</link> IOCTLs. + The flag has no effect on some devices / architectures. + </entry> </row> <row> <entry><constant>V4L2_BUF_FLAG_LAST</constant></entry>
include/trace/events/v4l2.h +1 -2
@@ -78,8 +78,7 @@ SHOW_FIELD { V4L2_BUF_FLAG_ERROR, "ERROR" }, \ { V4L2_BUF_FLAG_TIMECODE, "TIMECODE" }, \ { V4L2_BUF_FLAG_PREPARED, "PREPARED" }, \ - { V4L2_BUF_FLAG_NO_CACHE_INVALIDATE, "NO_CACHE_INVALIDATE" }, \ - { V4L2_BUF_FLAG_NO_CACHE_CLEAN, "NO_CACHE_CLEAN" }, \ + { V4L2_BUF_FLAG_NO_CACHE_SYNC, "NO_CACHE_SYNC" }, \ { V4L2_BUF_FLAG_TIMESTAMP_MASK, "TIMESTAMP_MASK" }, \ { V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN, "TIMESTAMP_UNKNOWN" }, \ { V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, "TIMESTAMP_MONOTONIC" }, \
include/uapi/linux/videodev2.h +5 -2
@@ -875,8 +875,11 @@ struct v4l2_buffer {#define V4L2_BUF_FLAG_TIMECODE 0x00000100/* Buffer is prepared for queuing */ #define V4L2_BUF_FLAG_PREPARED 0x00000400 -/* Cache handling flags */ -#define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x00000800 +/* Cache sync hint */ +#define V4L2_BUF_FLAG_NO_CACHE_SYNC 0x00000800 +/* DEPRECATED. THIS WILL BE REMOVED IN THE FUTURE! */ +#define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE V4L2_BUF_FLAG_NO_CACHE_SYNC +/* DEPRECATED. THIS WILL BE REMOVED IN THE FUTURE! */ #define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x00001000/* Timestamp type */ #define V4L2_BUF_FLAG_TIMESTAMP_MASK 0x0000e000
Signature · 1 line
2.1.0.231.g7484e3b
RFC 5/11 v4l2-core: Don't sync cache for a buffer if so requested
In reply to Sakari AilusFrom: Samu Onkalo <[email protected]>
The user may request to the driver (vb2) to skip the cache maintenance operations in case the buffer does not need cache synchronisation, e.g. in cases where the buffer is passed between hardware blocks without it being touched by the CPU.
Also document that the prepare and finish vb2_mem_ops might not get called every time the buffer ownership changes between the kernel and the user space.
--- drivers/media/v4l2-core/videobuf2-core.c | 52 +++++++++++++++++++++++++------- include/media/videobuf2-core.h | 12 +++++--- 2 files changed, 49 insertions(+), 15 deletions(-)
drivers/media/v4l2-core/videobuf2-core.c +41 -11
@@ -187,6 +187,28 @@ static void __vb2_queue_cancel(struct vb2_queue *q);static void __enqueue_in_driver (struct vb2_buffer *vb);/** +* __mem_prepare_planes() - call finish mem op for all planes of the buffer +*/ +static void __mem_prepare_planes (struct vb2_buffer *vb) +{ +unsigned int plane; + +for (plane =0 ; plane < vb->num_planes; ++plane) +call_void_memop (vb, prepare, vb->planes[plane].mem_priv); +} + +/** +* __mem_finish_planes() - call finish mem op for all planes of the buffer +*/ +static void __mem_finish_planes (struct vb2_buffer *vb) +{ +unsigned int plane; + +for (plane =0 ; plane < vb->num_planes; ++plane) +call_void_memop (vb, finish, vb->planes[plane].mem_priv); +} + +/** * __vb2_buf_mem_alloc() - allocate video memory for the given buffer */ static int __vb2_buf_mem_alloc (struct vb2_buffer *vb)
@@ -1391,6 +1413,10 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *bstatic int __prepare_mmap (struct vb2_buffer *vb,const struct v4l2_buffer *b) {__fill_vb2_buffer (vb, b, vb->v4l2_planes); + +if (!(b->flags &V4L2_BUF_FLAG_NO_CACHE_SYNC )) +__mem_prepare_planes (vb); +return call_vb_qop (vb, buf_prepare, vb); }
@@ -1476,6 +1502,11 @@ static int __prepare_userptr(struct vb2_buffer *vb,dprintk (1 ,"buffer initialization failed\n" ); goto err; } + +/* This is new buffer memory --- always synchronise cache. */ +__mem_prepare_planes (vb); + }else if (!(b->flags &V4L2_BUF_FLAG_NO_CACHE_SYNC )) { +__mem_prepare_planes (vb); } ret =call_vb_qop (vb, buf_prepare, vb);
@@ -1601,6 +1632,11 @@ static int __prepare_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)dprintk (1 ,"buffer initialization failed\n" ); goto err; } + +/* This is new buffer memory --- always synchronise cache. */ +__mem_prepare_planes (vb); + }else if (!(b->flags &V4L2_BUF_FLAG_NO_CACHE_SYNC )) { +__mem_prepare_planes (vb); } ret =call_vb_qop (vb, buf_prepare, vb);
@@ -1624,7 +1660,6 @@ err:static void __enqueue_in_driver (struct vb2_buffer *vb) {struct vb2_queue *q = vb->vb2_queue; -unsigned int plane; vb->state =VB2_BUF_STATE_ACTIVE ;atomic_inc (&q->owned_by_drv_count);
@@ -1691,10 +1726,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)return ret; } -/* sync buffers */ -for (plane =0 ; plane < vb->num_planes; ++plane) -call_void_memop (vb, prepare, vb->planes[plane].mem_priv); - vb->state =VB2_BUF_STATE_PREPARED ;return 0 ;
@@ -2078,7 +2109,7 @@ EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);/** * __vb2_dqbuf() - bring back the buffer to the DEQUEUED state */ -static void __vb2_dqbuf (struct vb2_buffer *vb) +static void __vb2_dqbuf (struct vb2_buffer *vb,bool no_cache_sync) {struct vb2_queue *q = vb->vb2_queue;unsigned int plane;
@@ -2089,9 +2120,8 @@ static void __vb2_dqbuf(struct vb2_buffer *vb) vb->state =VB2_BUF_STATE_DEQUEUED ; -/* sync buffers */ -for (plane =0 ; plane < vb->num_planes; plane++) -call_void_memop (vb, finish, vb->planes[plane].mem_priv); +if (!no_cache_sync) +__mem_finish_planes (vb);/* unmap DMABUF buffer */ if (q->memory ==V4L2_MEMORY_DMABUF )
@@ -2143,7 +2173,7 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n vb->v4l2_buf.flags & V4L2_BUF_FLAG_LAST) q->last_buffer_dequeued = true;/* go back to dequeued state */ -__vb2_dqbuf (vb); +__vb2_dqbuf (vb, b->flags &V4L2_BUF_FLAG_NO_CACHE_SYNC );dprintk (1 ,"dqbuf of buffer %d, with state %d\n" , vb->v4l2_buf.index, vb->state);
@@ -2246,7 +2276,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) vb->state =VB2_BUF_STATE_PREPARED ;call_void_vb_qop (vb, buf_finish, vb); } -__vb2_dqbuf (vb); +__vb2_dqbuf (vb, false); } }
include/media/videobuf2-core.h +8 -4
@@ -59,10 +59,14 @@ struct vb2_threadio_data;* dmabuf. * @unmap_dmabuf: releases access control to the dmabuf - allocator is notified * that this driver is done using the dmabuf for now. -* @prepare: called every time the buffer is passed from userspace to the -* driver, useful for cache synchronisation, optional. -* @finish: called every time the buffer is passed back from the driver -* to the userspace, also optional. +* @prepare: Called on the plane when the buffer ownership is passed from +* the user space to the kernel and the plane must be cache +* syncronised. The V4L2_BUF_FLAG_NO_CACHE_SYNC buffer flag may +* be used to skip this call. Optional. +* @finish: Called on the plane when the buffer ownership is passed from +* the kernel to the user space and the plane must be cache +* syncronised. The V4L2_BUF_FLAG_NO_CACHE_SYNC buffer flag may +* be used to skip this call. Optional. * @vaddr: return a kernel virtual address to a given memory buffer * associated with the passed private structure or NULL if no * such mapping exists.
Signature · 1 line
2.1.0.231.g7484e3b
RFC 6/11 vb2: Improve struct vb2_mem_ops documentation; alloc and put are for MMAP
In reply to Sakari AilusThe alloc() and put() ops are for MMAP buffers only. Document it.
--- include/media/videobuf2-core.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
include/media/videobuf2-core.h +10 -10
@@ -24,16 +24,16 @@ struct vb2_threadio_data;/** * struct vb2_mem_ops - memory handling/memory allocator operations -* @alloc: allocate video memory and, optionally, allocator private data, -* return NULL on failure or a pointer to allocator private, -* per-buffer data on success; the returned private structure -* will then be passed as buf_priv argument to other ops in this -* structure. Additional gfp_flags to use when allocating the -* are also passed to this operation. These flags are from the -* gfp_flags field of vb2_queue. -* @put: inform the allocator that the buffer will no longer be used; -* usually will result in the allocator freeing the buffer (if -* no other users of this buffer are present); the buf_priv +* @alloc: allocate video memory for an MMAP buffer and, optionally, +* allocator private data, return NULL on failure or a pointer +* to allocator private, per-buffer data on success; the returned +* private structure will then be passed as buf_priv argument to +* other ops in this structure. Additional gfp_flags to use when +* allocating the are also passed to this operation. These flags +* are from the gfp_flags field of vb2_queue. +* @put: inform the allocator that the MMAP buffer will no longer be +* used; usually will result in the allocator freeing the buffer +* (if no other users of this buffer are present); the buf_priv * argument is the allocator private per-buffer structure * previously returned from the alloc callback. * @get_userptr: acquire userspace memory for a hardware operation; used for
Signature · 1 line
2.1.0.231.g7484e3b
RFC 7/11 vb2: dma-contig: Remove redundant sgt_base field
In reply to Sakari AilusThe struct vb2_dc_buf contains two struct sg_table fields: sgt_base and dma_sgt. The former is used by DMA-BUF buffers whereas the latter is used by USERPTR.
Unify the two, leaving dma_sgt.
MMAP buffers do not need cache flushing since they have been allocated using dma_alloc_coherent().
--- drivers/media/v4l2-core/videobuf2-dma-contig.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-)
drivers/media/v4l2-core/videobuf2-dma-contig.c +10 -11
@@ -36,7 +36,6 @@ struct vb2_dc_buf {/* MMAP related */ struct vb2_vmarea_handler handler;atomic_t refcount; -struct sg_table *sgt_base;/* USERPTR related */ struct vm_area_struct *vma;
@@ -117,7 +116,7 @@ static void vb2_dc_prepare(void *buf_priv)struct sg_table *sgt = buf->dma_sgt;/* DMABUF exporter will flush the cache for us */ -if (!sgt || buf->db_attach) +if (!buf->vma)return ;dma_sync_sg_for_device (buf->dev, sgt->sgl, sgt->nents, buf->dma_dir);
@@ -129,7 +128,7 @@ static void vb2_dc_finish(void *buf_priv)struct sg_table *sgt = buf->dma_sgt;/* DMABUF exporter will flush the cache for us */ -if (!sgt || buf->db_attach) +if (!buf->vma)return ;dma_sync_sg_for_cpu (buf->dev, sgt->sgl, sgt->nents, buf->dma_dir);
@@ -146,9 +145,9 @@ static void vb2_dc_put(void *buf_priv)if (!atomic_dec_and_test (&buf->refcount))return ; -if (buf->sgt_base) { -sg_free_table (buf->sgt_base); -kfree (buf->sgt_base); +if (buf->dma_sgt) { +sg_free_table (buf->dma_sgt); +kfree (buf->dma_sgt); }dma_free_coherent (buf->dev, buf->size, buf->vaddr, buf->dma_addr);put_device (buf->dev);
@@ -252,13 +251,13 @@ static int vb2_dc_dmabuf_ops_attach(struct dma_buf *dbuf, struct device *dev,/* Copy the buf->base_sgt scatter list to the attachment, as we can't * map the same scatter list to multiple attachments at the same time. */ - ret =sg_alloc_table (sgt, buf->sgt_base->orig_nents,GFP_KERNEL ); + ret =sg_alloc_table (sgt, buf->dma_sgt->orig_nents,GFP_KERNEL );if (ret) {kfree (attach);return -ENOMEM ; } - rd = buf->sgt_base->sgl; + rd = buf->dma_sgt->sgl; wr = sgt->sgl;for (i =0 ; i < sgt->orig_nents; ++i) {sg_set_page (wr,sg_page (rd), rd->length, rd->offset);
@@ -409,10 +408,10 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) exp_info.flags = flags; exp_info.priv = buf; -if (!buf->sgt_base) - buf->sgt_base =vb2_dc_get_base_sgt (buf); +if (!buf->dma_sgt) + buf->dma_sgt =vb2_dc_get_base_sgt (buf); -if (WARN_ON (!buf->sgt_base)) +if (WARN_ON (!buf->dma_sgt))return NULL ; dbuf =dma_buf_export (&exp_info);
Signature · 1 line
2.1.0.231.g7484e3b
RFC 8/11 vb2: dma-contig: Move vb2_dc_get_base_sgt() up
In reply to Sakari AilusJust move the function up. It'll be soon needed earlier than previously.
--- drivers/media/v4l2-core/videobuf2-dma-contig.c | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-)
drivers/media/v4l2-core/videobuf2-dma-contig.c +22 -22
@@ -82,6 +82,28 @@ static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt)return size; } +static struct sg_table *vb2_dc_get_base_sgt (struct vb2_dc_buf *buf) +{ +int ret; +struct sg_table *sgt; + + sgt =kmalloc (sizeof (*sgt),GFP_KERNEL ); +if (!sgt) { +dev_err (buf->dev,"failed to alloc sg table\n" ); +return NULL ; + } + + ret =dma_get_sgtable (buf->dev, sgt, buf->vaddr, buf->dma_addr, + buf->size); +if (ret <0 ) { +dev_err (buf->dev,"failed to get scatterlist from DMA API\n" ); +kfree (sgt); +return NULL ; + } + +return sgt; +} +/*********************************************/ /* callbacks for all buffers */ /*********************************************/
@@ -375,28 +397,6 @@ static struct dma_buf_ops vb2_dc_dmabuf_ops = { .release = vb2_dc_dmabuf_ops_release, }; -static struct sg_table *vb2_dc_get_base_sgt (struct vb2_dc_buf *buf) -{ -int ret; -struct sg_table *sgt; - - sgt =kmalloc (sizeof (*sgt),GFP_KERNEL ); -if (!sgt) { -dev_err (buf->dev,"failed to alloc sg table\n" ); -return NULL ; - } - - ret =dma_get_sgtable (buf->dev, sgt, buf->vaddr, buf->dma_addr, - buf->size); -if (ret <0 ) { -dev_err (buf->dev,"failed to get scatterlist from DMA API\n" ); -kfree (sgt); -return NULL ; - } - -return sgt; -} -static struct dma_buf *vb2_dc_get_dmabuf (void *buf_priv,unsigned long flags) {struct vb2_dc_buf *buf = buf_priv;
Signature · 1 line
2.1.0.231.g7484e3b
RFC 9/11 vb2: dma-contig: Don't warn on failure in obtaining scatterlist
In reply to Sakari Ailusvb2_dc_get_base_sgt() which obtains the scatterlist already prints information on why the scatterlist could not be obtained.
Also, remove the useless warning of a failed kmalloc().
--- drivers/media/v4l2-core/videobuf2-dma-contig.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
drivers/media/v4l2-core/videobuf2-dma-contig.c +2 -4
@@ -88,10 +88,8 @@ static struct sg_table *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf)struct sg_table *sgt; sgt =kmalloc (sizeof (*sgt),GFP_KERNEL ); -if (!sgt) { -dev_err (buf->dev,"failed to alloc sg table\n" ); +if (!sgt)return NULL ; - } ret =dma_get_sgtable (buf->dev, sgt, buf->vaddr, buf->dma_addr, buf->size);
@@ -411,7 +409,7 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags)if (!buf->dma_sgt) buf->dma_sgt =vb2_dc_get_base_sgt (buf); -if (WARN_ON (!buf->dma_sgt)) +if (!buf->dma_sgt)return NULL ; dbuf =dma_buf_export (&exp_info);
Signature · 1 line
2.1.0.231.g7484e3b
RFC 10/11 vb2: dma-contig: Let drivers decide DMA attrs of MMAP and USERPTR bufs
In reply to Sakari AilusThe desirable DMA attributes are not generic for all devices using Videobuf2 contiguous DMA ops. Let the drivers decide.
This change also results in MMAP buffers always having an sg_table (dma_sgt field).
Also arrange the header files alphabetically.
As a result, also the DMA-BUF exporter must provide ops for synchronising the cache. This adds begin_cpu_access and end_cpu_access ops to vb2_dc_dmabuf_ops.
--- drivers/media/v4l2-core/videobuf2-dma-contig.c | 104 ++++++++++++++++++------- include/media/videobuf2-dma-contig.h | 3 +- 2 files changed, 79 insertions(+), 28 deletions(-)
drivers/media/v4l2-core/videobuf2-dma-contig.c +77 -27
@@ -11,11 +11,11 @@*/ #include <linux/dma-buf.h> +#include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/scatterlist.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/dma-mapping.h> #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h>
@@ -23,6 +23,7 @@struct vb2_dc_conf {struct device *dev; +struct dma_attrs attrs; };struct vb2_dc_buf {
@@ -34,6 +35,7 @@ struct vb2_dc_buf {struct sg_table *dma_sgt;/* MMAP related */ +struct dma_attrs *attrs;struct vb2_vmarea_handler handler;atomic_t refcount;
@@ -135,8 +137,12 @@ static void vb2_dc_prepare(void *buf_priv)struct vb2_dc_buf *buf = buf_priv;struct sg_table *sgt = buf->dma_sgt; -/* DMABUF exporter will flush the cache for us */ -if (!buf->vma) +/* +* DMABUF exporter will flush the cache for us; only USERPTR +* and MMAP buffers with non-coherent memory will be flushed. +*/ +if (!buf->attrs || + !dma_get_attr (DMA_ATTR_NON_CONSISTENT , buf->attrs))return ;dma_sync_sg_for_device (buf->dev, sgt->sgl, sgt->nents, buf->dma_dir);
@@ -147,8 +153,12 @@ static void vb2_dc_finish(void *buf_priv)struct vb2_dc_buf *buf = buf_priv;struct sg_table *sgt = buf->dma_sgt; -/* DMABUF exporter will flush the cache for us */ -if (!buf->vma) +/* +* DMABUF exporter will flush the cache for us; only USERPTR +* and MMAP buffers with non-coherent memory will be flushed. +*/ +if (!buf->attrs || + !dma_get_attr (DMA_ATTR_NON_CONSISTENT , buf->attrs))return ;dma_sync_sg_for_cpu (buf->dev, sgt->sgl, sgt->nents, buf->dma_dir);
@@ -169,7 +179,8 @@ static void vb2_dc_put(void *buf_priv)sg_free_table (buf->dma_sgt);kfree (buf->dma_sgt); } -dma_free_coherent (buf->dev, buf->size, buf->vaddr, buf->dma_addr); +dma_free_attrs (buf->dev, buf->size, buf->vaddr, buf->dma_addr, + buf->attrs);put_device (buf->dev);kfree (buf); }
@@ -185,14 +196,25 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size,if (!buf)return ERR_PTR (-ENOMEM ); - buf->vaddr =dma_alloc_coherent (dev, size, &buf->dma_addr, -GFP_KERNEL | gfp_flags); + buf->attrs = &conf->attrs; + + buf->vaddr =dma_alloc_attrs (dev, size, &buf->dma_addr, +GFP_KERNEL | gfp_flags, buf->attrs);if (!buf->vaddr) { -dev_err (dev,"dma_alloc_coherent of size %ld failed\n" , size); +dev_err (dev,"dma_alloc_attrs of size %ld failed\n" , size);kfree (buf);return ERR_PTR (-ENOMEM ); } +if (dma_get_attr (DMA_ATTR_NON_CONSISTENT , buf->attrs)) { + buf->dma_sgt =vb2_dc_get_base_sgt (buf); +if (!buf->dma_sgt) { +dma_free_attrs (dev, size, buf->vaddr, buf->dma_addr, + buf->attrs); +return ERR_PTR (-ENOMEM ); + } + } +/* Prevent the device from being released while the buffer is used */ buf->dev =get_device (dev); buf->size = size;
@@ -223,8 +245,8 @@ static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma)*/ vma->vm_pgoff =0 ; - ret =dma_mmap_coherent (buf->dev, vma, buf->vaddr, - buf->dma_addr, buf->size); + ret =dma_mmap_attrs (buf->dev, vma, buf->vaddr, buf->dma_addr, + buf->size, buf->attrs);if (ret) {pr_err ("Remapping memory failed, error: %d\n" , ret);
@@ -370,6 +392,34 @@ static void *vb2_dc_dmabuf_ops_kmap(struct dma_buf *dbuf, unsigned long pgnum)return buf->vaddr + pgnum *PAGE_SIZE ; } +static int vb2_dc_dmabuf_ops_begin_cpu_access ( +struct dma_buf *dbuf,size_t start,size_t len, +enum dma_data_direction direction) +{ +struct vb2_dc_buf *buf = dbuf->priv; +struct sg_table *sgt = buf->dma_sgt; + +if (!dma_get_attr (DMA_ATTR_NON_CONSISTENT , buf->attrs)) +return 0 ; + +dma_sync_sg_for_cpu (buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + +return 0 ; +} + +static void vb2_dc_dmabuf_ops_end_cpu_access ( +struct dma_buf *dbuf,size_t start,size_t len, +enum dma_data_direction direction) +{ +struct vb2_dc_buf *buf = dbuf->priv; +struct sg_table *sgt = buf->dma_sgt; + +if (!dma_get_attr (DMA_ATTR_NON_CONSISTENT , buf->attrs)) +return ; + +dma_sync_sg_for_device (buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); +} +static void *vb2_dc_dmabuf_ops_vmap (struct dma_buf *dbuf) {struct vb2_dc_buf *buf = dbuf->priv;
@@ -390,6 +440,8 @@ static struct dma_buf_ops vb2_dc_dmabuf_ops = { .unmap_dma_buf = vb2_dc_dmabuf_ops_unmap, .kmap = vb2_dc_dmabuf_ops_kmap, .kmap_atomic = vb2_dc_dmabuf_ops_kmap, + .begin_cpu_access = vb2_dc_dmabuf_ops_begin_cpu_access, + .end_cpu_access = vb2_dc_dmabuf_ops_end_cpu_access, .vmap = vb2_dc_dmabuf_ops_vmap, .mmap = vb2_dc_dmabuf_ops_mmap, .release = vb2_dc_dmabuf_ops_release,
@@ -514,15 +566,13 @@ static void vb2_dc_put_userptr(void *buf_priv)struct sg_table *sgt = buf->dma_sgt;if (sgt) { -DEFINE_DMA_ATTRS (attrs); - -dma_set_attr (DMA_ATTR_SKIP_CPU_SYNC , &attrs);/* -* No need to sync to CPU, it's already synced to the CPU -* since the finish() memop will have been called before this. +* Don't ask to skip cache sync in case if the user +* did ask to skip cache flush the last time the +* buffer was dequeued. */ dma_unmap_sg_attrs (buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir, &attrs); + buf->dma_dir, buf->attrs);if (!vma_is_io (buf->vma))vb2_dc_sgt_foreach_page (sgt, vb2_dc_put_dirty_page);
@@ -579,9 +629,6 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,struct sg_table *sgt;unsigned long contig_size;unsigned long dma_align =dma_get_cache_alignment (); -DEFINE_DMA_ATTRS (attrs); - -dma_set_attr (DMA_ATTR_SKIP_CPU_SYNC , &attrs);/* Only cache aligned DMA transfers are reliable */ if (!IS_ALIGNED (vaddr | size, dma_align)) {
@@ -598,6 +645,8 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,if (!buf)return ERR_PTR (-ENOMEM ); + buf->attrs = &conf->attrs; + buf->dev = conf->dev; buf->dma_dir = dma_dir;
@@ -667,12 +716,8 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,kfree (pages); pages =NULL ; -/* -* No need to sync to the device, this will happen later when the -* prepare() memop is called. -*/ sgt->nents =dma_map_sg_attrs (buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir, &attrs); + buf->dma_dir, buf->attrs);if (sgt->nents <=0 ) {pr_err ("failed to map scatterlist\n" ); ret = -EIO ;
@@ -695,7 +740,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, fail_map_sg:dma_unmap_sg_attrs (buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir, &attrs); + buf->dma_dir, buf->attrs); fail_sgt_init:if (!vma_is_io (buf->vma))
@@ -856,7 +901,7 @@ const struct vb2_mem_ops vb2_dma_contig_memops = { };EXPORT_SYMBOL_GPL (vb2_dma_contig_memops); -void *vb2_dma_contig_init_ctx (struct device *dev) +void *vb2_dma_contig_init_ctx (struct device *dev,struct dma_attrs *attrs) {struct vb2_dc_conf *conf;
@@ -866,6 +911,11 @@ void *vb2_dma_contig_init_ctx(struct device *dev) conf->dev = dev; +if (!attrs) +init_dma_attrs (&conf->attrs); +else + conf->attrs = *attrs; +return conf; }EXPORT_SYMBOL_GPL (vb2_dma_contig_init_ctx);
include/media/videobuf2-dma-contig.h +2 -1
@@ -24,7 +24,8 @@ vb2_dma_contig_plane_dma_addr(struct vb2_buffer *vb, unsigned int plane_no)return *addr; } -void *vb2_dma_contig_init_ctx (struct device *dev); +void *vb2_dma_contig_init_ctx (struct device *dev, +struct dma_attrs *attrs);void vb2_dma_contig_cleanup_ctx (void *alloc_ctx);extern const struct vb2_mem_ops vb2_dma_contig_memops;
Signature · 1 line
2.1.0.231.g7484e3b
RFC 11/11 vb2: dma-contig: Add WARN_ON_ONCE() to check for potential bugs
In reply to Sakari AilusThe scatterlist should always be present when the cache would need to be flushed. Each buffer type has its own means to provide that. Add WARN_ON_ONCE() to check the scatterist exists.
--- drivers/media/v4l2-core/videobuf2-dma-contig.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/media/v4l2-core/videobuf2-dma-contig.c +6 -0
@@ -145,6 +145,9 @@ static void vb2_dc_prepare(void *buf_priv) !dma_get_attr(DMA_ATTR_NON_CONSISTENT, buf->attrs)) return; +if (WARN_ON_ONCE (!sgt)) +return ; +dma_sync_sg_for_device (buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); }
@@ -161,6 +164,9 @@ static void vb2_dc_finish(void *buf_priv) !dma_get_attr(DMA_ATTR_NON_CONSISTENT, buf->attrs)) return; +if (WARN_ON_ONCE (!sgt)) +return ; +dma_sync_sg_for_cpu (buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); }
Signature · 1 line
2.1.0.231.g7484e3b
+ Sumit, Rob, Laura and Daniel who have discussed cache optimization with me at LPC in the context of dmabuf.
The full patch set can be found at https://www.mail-archive.com/[email protected]/msg92120.html
Quoted 18 lines · On Tuesday 08 September 2015 13:33:44 Sakari Ailus wrote:
Hi folks,
This RFC patchset achieves two main objectives:
1. Respects cache flags passed from the user space. As no driver nor videobuf2 has (ever?) implemented them, the two flags are replaced by a single one (
V4L2_BUF_FLAG_NO_CACHE_SYNC) and the two old flags are deprecated. This is done since a single flag provides the driver with enough information on what to do. (See more info in patch 4.)2. Allows a driver using videobuf2 dma-contig memory type to choose whether it prefers coherent or non-coherent CPU access to buffer memory for MMAP and USERPTR buffers. This could be later extended to be specified by the user, and per buffer if needed.
Only dma-contig memory type is changed but the same could be done to dma-sg as well. I can add it to the set if people are happy with the changes to dma-contig.
Signature · 3 lines
Regards, Laurent Pinchart