dma-buf synchronization patches
Subsystems (17)
cover dma-buf synchronization patches
The following series implements fence and converts dma-buf and android sync to use it. Patch 6 and 7 add support for polling to dma-buf, blocking until all fences are signaled.
---
Maarten Lankhorst (7):
sched: allow try_to_wake_up to be used internally outside of core.c
fence: dma-buf cross-device synchronization (v16)
seqno-fence: Hardware dma-buf implementation of fencing (v4)
dma-buf: use reservation objects
android: convert sync to fence api, v3
reservation: add support for fences to enable cross-device synchronisation
dma-buf: add poll support
Documentation/DocBook/device-drivers.tmpl | 3 drivers/base/Kconfig | 9 drivers/base/Makefile | 2 drivers/base/dma-buf.c | 124 +++ drivers/base/fence.c | 465 ++++++++++++ drivers/gpu/drm/drm_prime.c | 8 drivers/gpu/drm/exynos/exynos_drm_dmabuf.c | 2 drivers/gpu/drm/i915/i915_gem_dmabuf.c | 2 drivers/gpu/drm/nouveau/nouveau_drm.c | 1 drivers/gpu/drm/nouveau/nouveau_gem.h | 1 drivers/gpu/drm/nouveau/nouveau_prime.c | 7 drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c | 2 drivers/gpu/drm/radeon/radeon_drv.c | 2 drivers/gpu/drm/radeon/radeon_prime.c | 8 drivers/gpu/drm/ttm/ttm_object.c | 2 drivers/media/v4l2-core/videobuf2-dma-contig.c | 2 drivers/staging/android/Kconfig | 1 drivers/staging/android/Makefile | 2 drivers/staging/android/sw_sync.c | 4 drivers/staging/android/sync.c | 895 ++++++++---------------- drivers/staging/android/sync.h | 85 +- drivers/staging/android/sync_debug.c | 245 +++++++ drivers/staging/android/trace/sync.h | 12 include/drm/drmP.h | 2 include/linux/dma-buf.h | 21 - include/linux/fence.h | 329 +++++++++ include/linux/reservation.h | 18 include/linux/seqno-fence.h | 109 +++ include/linux/wait.h | 1 include/trace/events/fence.h | 125 +++ kernel/sched/core.c | 2 31 files changed, 1825 insertions(+), 666 deletions(-) create mode 100644 drivers/base/fence.c create mode 100644 drivers/staging/android/sync_debug.c create mode 100644 include/linux/fence.h create mode 100644 include/linux/seqno-fence.h create mode 100644 include/trace/events/fence.h
Signature · 1 line
Signature
PATCH 1/7 sched: allow try_to_wake_up to be used internally outside of core.c
The kernel fence implementation doesn't use event queues, but needs to perform the same wake up. The symbol is not exported, since the fence implementation is not built as a module.
--- include/linux/wait.h | 1 + kernel/sched/core.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-)
include/linux/wait.h +1 -0
@@ -12,6 +12,7 @@typedef struct __wait_queue wait_queue_t ;typedef int (*wait_queue_func_t )(wait_queue_t *wait,unsigned mode,int flags,void *key);int default_wake_function (wait_queue_t *wait,unsigned mode,int flags,void *key); +int try_to_wake_up (struct task_struct *p,unsigned int state,int wake_flags);struct __wait_queue {unsigned int flags;
kernel/sched/core.c +1 -1
@@ -1578,7 +1578,7 @@ static void ttwu_queue(struct task_struct *p, int cpu)* Return: %true if @p was woken up, %false if it was already running. * or @state didn't match @p's state. */ -static int +int try_to_wake_up (struct task_struct *p,unsigned int state,int wake_flags) {unsigned long flags;
Re: [PATCH 1/7] sched: allow try_to_wake_up to be used internally outside of core.c
Quoted 28 lines · Maarten Lankhorst
The kernel fence implementation doesn't use event queues, but needs to perform the same wake up. The symbol is not exported, since the fence implementation is not built as a module.
Signed-off-by Maarten Lankhorst <[email protected]>--- include/linux/wait.h | 1 + kernel/sched/core.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-)include/linux/wait.h +1
-0@@ -12,6 +12,7 @@typedef struct __wait_queue wait_queue_t ;typedef int (*wait_queue_func_t )(wait_queue_t *wait,unsigned mode,int flags,void *key);int default_wake_function (wait_queue_t *wait,unsigned mode,int flags,void *key); +int try_to_wake_up (struct task_struct *p,unsigned int state,int wake_flags);struct __wait_queue {unsigned int flags;kernel/sched/core.c +0
-0
kernel/sched/core.c static void ttwu_queue(struct task_struct *p, int cpu) · line 1584
Quoted 8 lines
kernel/sched/core.c +1
-1@@ -1578,7 +1578,7 @@ static void ttwu_queue(struct task_struct *p, int cpu)* Return: %true if @p was woken up, %false if it was already running. * or @state didn't match @p's state. */ -static int +int try_to_wake_up (struct task_struct *p,unsigned int state,int wake_flags) {unsigned long flags;
wake_up_state is already available in linux/sched.h, can you use that?
Re: [PATCH 1/7] sched: allow try_to_wake_up to be used internally outside of core.c
op 13-01-14 19:50, Colin Cross schreef:
Quoted 31 lines
Quoted 29 lines · Maarten Lankhorst
The kernel fence implementation doesn't use event queues, but needs to perform the same wake up. The symbol is not exported, since the fence implementation is not built as a module.
Signed-off-by Maarten Lankhorst <[email protected]>--- include/linux/wait.h | 1 + kernel/sched/core.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-)include/linux/wait.h +1
-0@@ -12,6 +12,7 @@typedef struct __wait_queue wait_queue_t ;typedef int (*wait_queue_func_t )(wait_queue_t *wait,unsigned mode,int flags,void *key);int default_wake_function (wait_queue_t *wait,unsigned mode,int flags,void *key); +int try_to_wake_up (struct task_struct *p,unsigned int state,int wake_flags);struct __wait_queue {unsigned int flags;kernel/sched/core.c +0
-0@@ -1578,7 +1578,7 @@ static void ttwu_queue(struct task_struct *p, int cpu)* Return: %true if @p was woken up, %false if it was already running. * or @state didn't match @p's state.
Quoted 9 lines
kernel/sched/core.c static void ttwu_queue(struct task_struct *p, int cpu) · line 1584
Quoted 7 lines
kernel/sched/core.c +1
-1*/ -static int +int try_to_wake_up (struct task_struct *p,unsigned int state,int wake_flags) {unsigned long flags;
wake_up_stateis already available inlinux/sched.h, can you use that?
Indeed! Thanks for the catch.
~Maarten
PATCH 2/7 fence: dma-buf cross-device synchronization (v16)
In reply to Maarten LankhorstA fence can be attached to a buffer which is being filled or consumed by hw, to allow userspace to pass the buffer without waiting to another device. For example, userspace can call page_flip ioctl to display the next frame of graphics after kicking the GPU but while the GPU is still rendering. The display device sharing the buffer with the GPU would attach a callback to get notified when the GPU's rendering-complete IRQ fires, to update the scan-out address of the display, without having to wake up userspace.
A driver must allocate a fence context for each execution ring that can run in parallel. The function for this takes an argument with how many contexts to allocate:
+ fence_context_alloc()
A fence is transient, one-shot deal. It is allocated and attached to one or more dma-buf's. When the one that attached it is done, with the pending operation, it can signal the fence:
+ fence_signal()
To have a rough approximation whether a fence is fired, call:
+ fence_is_signaled()
The dma-buf-mgr handles tracking, and waiting on, the fences associated with a dma-buf.
The one pending on the fence can add an async callback:
+ fence_add_callback()
The callback can optionally be cancelled with:
+ fence_remove_callback()
To wait synchronously, optionally with a timeout:
+ fence_wait()
+ fence_wait_timeout()
When emitting a fence, call:
+ trace_fence_emit()
To annotate that a fence is blocking on another fence, call:
+ trace_fence_annotate_wait_on(fence, on_fence)
A default software-only implementation is provided, which can be used by drivers attaching a fence to a buffer when they have no other means for hw sync. But a memory backed fence is also envisioned, because it is common that GPU's can write to, or poll on some memory location for synchronization. For example:
fence = custom_get_fence(...);
if ((seqno_fence = to_seqno_fence(fence)) != NULL) {
dma_buf *fence_buf = fence->sync_buf;
get_dma_buf(fence_buf); ... tell the hw the memory location to wait ... custom_wait_on(fence_buf, fence->seqno_ofs, fence->seqno);
} else {
/* fall-back to sw sync * /
fence_add_callback(fence, my_cb);}
On SoC platforms, if some other hw mechanism is provided for synchronizing between IP blocks, it could be supported as an alternate implementation with it's own fence ops in a similar way.
enable_signaling callback is used to provide sw signaling in case a cpu waiter is requested or no compatible hardware signaling could be used.
The intention is to provide a userspace interface (presumably via eventfd) later, to be used in conjunction with dma-buf's mmap support for sw access to buffers (or for userspace apps that would prefer to do their own synchronization).
v1: Original
v2: After discussion w/ danvet and mlankhorst on #dri-devel, we decided
that dma-fence didn't need to care about the sw->hw signaling path (it can be handled same as sw->sw case), and therefore the fence->ops can be simplified and more handled in the core. So remove the signal, add_callback, cancel_callback, and wait ops, and replace with a simple enable_signaling() op which can be used to inform a fence supporting hw->hw signaling that one or more devices which do not support hw signaling are waiting (and therefore it should enable an irq or do whatever is necessary in order that the CPU is notified when the fence is passed).
v3: Fix locking fail in attach_fence() and get_fence()
v4: Remove tie-in w/ dma-buf.. after discussion w/ danvet and mlankorst
we decided that we need to be able to attach one fence to N dma-buf's, so using the list_head in dma-fence struct would be problematic.
v5: [ Maarten Lankhorst ] Updated for dma-bikeshed-fence and dma-buf-manager.
v6: [ Maarten Lankhorst ] I removed dma_fence_cancel_callback and some comments
about checking if fence fired or not. This is broken by design. waitqueue_active during destruction is now fatal, since the signaller should be holding a reference in enable_signalling until it signalled the fence. Pass the original dma_fence_cb along, and call __remove_wait in the dma_fence_callback handler, so that no cleanup needs to be performed.
v7: [ Maarten Lankhorst ] Set cb->func and only enable sw signaling if
fence wasn't signaled yet, for example for hardware fences that may choose to signal blindly.
v8: [ Maarten Lankhorst ] Tons of tiny fixes, moved __dma_fence_init to
header and fixed include mess. dma-fence.h now includes dma-buf.h All members are now initialized, so kmalloc can be used for allocating a dma-fence. More documentation added.
v9: Change compiler bitfields to flags, change return type of
enable_signaling to bool. Rework dma_fence_wait. Added dma_fence_is_signaled and dma_fence_wait_timeout.
s/dma// and change exports to non GPL. Added fence_is_signaled and fence_enable_sw_signaling calls, add ability to override default wait operation.
v10: remove event_queue, use a custom list, export try_to_wake_up from
scheduler. Remove fence lock and use a global spinlock instead, this should hopefully remove all the locking headaches I was having on trying to implement this. enable_signaling is called with this lock held.
v11:
Use atomic ops for flags, lifting the need for some spin_lock_irqsaves. However I kept the guarantee that after fence_signal returns, it is guaranteed that enable_signaling has either been called to completion, or will not be called any more.
Add contexts and seqno to base fence implementation. This allows you to wait for less fences, by testing for seqno + signaled, and then only wait on the later fence.
Add FENCE_TRACE, FENCE_WARN, and FENCE_ERR. This makes debugging easier. An CONFIG_DEBUG_FENCE will be added to turn off the FENCE_TRACE spam, and another runtime option can turn it off at runtime.
v12:
Add CONFIG_FENCE_TRACE. Add missing documentation for the fence->context
and fence->seqno members.v13:
Fixup CONFIG_FENCE_TRACE kconfig description. Move fence_context_alloc to fence. Simplify fence_later.
Kill priv member to fence_cb.
v14:
Remove priv argument from fence_add_callback, oops!
v15:
Remove priv from documentation.
Explicitly include linux/atomic.h.
v16:
Add trace events.
Import changes required by android syncpoints.
--- Documentation/DocBook/device-drivers.tmpl | 2 drivers/base/Kconfig | 9 + drivers/base/Makefile | 2 drivers/base/fence.c | 415 +++++++++++++++++++++++++++++ include/linux/fence.h | 329 +++++++++++++++++++++++ include/trace/events/fence.h | 125 +++++++++ 6 files changed, 881 insertions(+), 1 deletion(-) create mode 100644 drivers/base/fence.c create mode 100644 include/linux/fence.h create mode 100644 include/trace/events/fence.h
Documentation/DocBook/device-drivers.tmpl +2 -0
@@ -129,6 +129,8 @@ X!Edrivers/base/interface.c </sect1> <sect1><title>Device Drivers DMA Management</title> !Edrivers/base/dma-buf.c +!Edrivers/base/fence.c +!Iinclude/linux/fence.h !Edrivers/base/reservation.c !Iinclude/linux/reservation.h !Edrivers/base/dma-coherent.c
drivers/base/Kconfig +9 -0
@@ -200,6 +200,15 @@ config DMA_SHARED_BUFFER APIs extension; the file's descriptor can then be passed on to other driver. +config FENCE_TRACE + bool "Enable verbose FENCE_TRACE messages" + depends on DMA_SHARED_BUFFER + help + Enable the FENCE_TRACE printks. This will add extra + spam to the console log, but will make it easier to diagnose + lockup related problems for dma-buffers shared across multiple + devices. + config DMA_CMA bool "DMA Contiguous Memory Allocator" depends on HAVE_DMA_CONTIGUOUS && CMA
drivers/base/Makefile +1 -1
@@ -10,7 +10,7 @@ obj-$(CONFIG_DMA_CMA) += dma-contiguous.o obj-y += power/ obj-$(CONFIG_HAS_DMA) += dma-mapping.o obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o -obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o reservation.o +obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o fence.o reservation.o obj-$(CONFIG_ISA) += isa.o obj-$(CONFIG_FW_LOADER) += firmware_class.o obj-$(CONFIG_NUMA) += node.o
drivers/base/fence.c new file +415 -0
415 lines not on this page · raw message
include/linux/fence.h new file +329 -0
329 lines not on this page · raw message
include/trace/events/fence.h new file +125 -0
125 lines not on this page · raw message
PATCH 3/7 seqno-fence: Hardware dma-buf implementation of fencing (v4)
In reply to Maarten LankhorstThis type of fence can be used with hardware synchronization for simple hardware that can block execution until the condition (dma_buf[offset] - value) >= 0 has been met.
A software fallback still has to be provided in case the fence is used with a device that doesn't support this mechanism. It is useful to expose this for graphics cards that have an op to support this.
Some cards like i915 can export those, but don't have an option to wait, so they need the software fallback.
I extended the original patch by Rob Clark.
v1: Original
v2: Renamed from bikeshed to seqno, moved into dma-fence.c since
not much was left of the file. Lots of documentation added.
v3: Use fence_ops instead of custom callbacks. Moved to own file
to avoid circular dependency between dma-buf.h and fence.h
v4: Add spinlock pointer to seqno_fence_init
--- Documentation/DocBook/device-drivers.tmpl | 1 drivers/base/fence.c | 50 +++++++++++++ include/linux/seqno-fence.h | 109 +++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 include/linux/seqno-fence.h
Documentation/DocBook/device-drivers.tmpl +1 -0
@@ -131,6 +131,7 @@ X!Edrivers/base/interface.c !Edrivers/base/dma-buf.c !Edrivers/base/fence.c !Iinclude/linux/fence.h +!Iinclude/linux/seqno-fence.h !Edrivers/base/reservation.c !Iinclude/linux/reservation.h !Edrivers/base/dma-coherent.c
drivers/base/fence.c +50 -0
59 lines not on this page · raw message
include/linux/seqno-fence.h new file +109 -0
109 lines not on this page · raw message
PATCH 4/7 dma-buf: use reservation objects
In reply to Maarten LankhorstThis allows reservation objects to be used in dma-buf. it's required for implementing polling support on the fences that belong to a dma-buf.
--- drivers/base/dma-buf.c | 22 ++++++++++++++++++++-- drivers/gpu/drm/drm_prime.c | 8 +++++++- drivers/gpu/drm/exynos/exynos_drm_dmabuf.c | 2 +- drivers/gpu/drm/i915/i915_gem_dmabuf.c | 2 +- drivers/gpu/drm/nouveau/nouveau_drm.c | 1 + drivers/gpu/drm/nouveau/nouveau_gem.h | 1 + drivers/gpu/drm/nouveau/nouveau_prime.c | 7 +++++++ drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c | 2 +- drivers/gpu/drm/radeon/radeon_drv.c | 2 ++ drivers/gpu/drm/radeon/radeon_prime.c | 8 ++++++++ drivers/gpu/drm/ttm/ttm_object.c | 2 +- drivers/media/v4l2-core/videobuf2-dma-contig.c | 2 +- include/drm/drmP.h | 2 ++ include/linux/dma-buf.h | 9 ++++++--- 14 files changed, 59 insertions(+), 11 deletions(-)
drivers/base/dma-buf.c +20 -2
@@ -25,10 +25,12 @@#include <linux/fs.h> #include <linux/slab.h> #include <linux/dma-buf.h> +#include <linux/fence.h> #include <linux/anon_inodes.h> #include <linux/export.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/reservation.h> static inline int is_dma_buf_file (struct file *);
@@ -56,6 +58,9 @@ static int dma_buf_release(struct inode *inode, struct file *file)list_del (&dmabuf->list_node);mutex_unlock (&db_list.lock); +if (dmabuf->resv == (struct reservation_object *)&dmabuf[1 ]) +reservation_object_fini (dmabuf->resv); +kfree (dmabuf);return 0 ; }
@@ -128,6 +133,7 @@ static inline int is_dma_buf_file(struct file *file)* @size: [in] Size of the buffer * @flags: [in] mode flags for the file. * @exp_name: [in] name of the exporting module - useful for debugging. +* @resv: [in] reservation-object, NULL to allocate default one. * * Returns, on success, a newly created dma_buf object, which wraps the * supplied private data and operations for dma_buf_ops. On either missing
@@ -135,10 +141,17 @@ static inline int is_dma_buf_file(struct file *file)* */ struct dma_buf *dma_buf_export_named (void *priv,const struct dma_buf_ops *ops, -size_t size,int flags,const char *exp_name) +size_t size,int flags,const char *exp_name, +struct reservation_object *resv) {struct dma_buf *dmabuf;struct file *file; +size_t alloc_size =sizeof (struct dma_buf ); +if (!resv) + alloc_size +=sizeof (struct reservation_object ); +else +/* prevent &dma_buf[1] == dma_buf->resv */ + alloc_size +=1 ; if (WARN_ON(!priv || !ops || !ops->map_dma_buf
@@ -150,7 +163,7 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,return ERR_PTR (-EINVAL ); } - dmabuf =kzalloc (sizeof (struct dma_buf ),GFP_KERNEL ); + dmabuf =kzalloc (alloc_size,GFP_KERNEL );if (dmabuf ==NULL )return ERR_PTR (-ENOMEM );
@@ -158,6 +171,11 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, dmabuf->ops = ops; dmabuf->size = size; dmabuf->exp_name = exp_name; +if (!resv) { + resv = (struct reservation_object *)&dmabuf[1 ]; +reservation_object_init (resv); + } + dmabuf->resv = resv; file =anon_inode_getfile ("dmabuf" , &dma_buf_fops, dmabuf, flags);if (IS_ERR (file)) {
drivers/gpu/drm/drm_prime.c +7 -1
@@ -318,7 +318,13 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {struct dma_buf *drm_gem_prime_export (struct drm_device *dev,struct drm_gem_object *obj,int flags) { -return dma_buf_export (obj, &drm_gem_prime_dmabuf_ops, obj->size, flags); +struct reservation_object *robj =NULL ; + +if (dev->driver->gem_prime_res_obj) + robj = dev->driver->gem_prime_res_obj (obj); + +return dma_buf_export (obj, &drm_gem_prime_dmabuf_ops, obj->size, + flags, robj); }EXPORT_SYMBOL (drm_gem_prime_export);
drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +1 -1
@@ -187,7 +187,7 @@ struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev,struct exynos_drm_gem_obj *exynos_gem_obj =to_exynos_gem_obj (obj);return dma_buf_export (obj, &exynos_dmabuf_ops, - exynos_gem_obj->base.size, flags); + exynos_gem_obj->base.size, flags,NULL ); } struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
drivers/gpu/drm/i915/i915_gem_dmabuf.c +1 -1
@@ -233,7 +233,7 @@ static const struct dma_buf_ops i915_dmabuf_ops = {struct dma_buf *i915_gem_prime_export (struct drm_device *dev,struct drm_gem_object *gem_obj,int flags) { -return dma_buf_export (gem_obj, &i915_dmabuf_ops, gem_obj->size, flags); +return dma_buf_export (gem_obj, &i915_dmabuf_ops, gem_obj->size, flags,NULL ); }static int i915_gem_object_get_pages_dmabuf (struct drm_i915_gem_object *obj)
drivers/gpu/drm/nouveau/nouveau_drm.c +1 -0
@@ -808,6 +808,7 @@ driver = { .gem_prime_export = drm_gem_prime_export, .gem_prime_import = drm_gem_prime_import, .gem_prime_pin = nouveau_gem_prime_pin, + .gem_prime_res_obj = nouveau_gem_prime_res_obj, .gem_prime_unpin = nouveau_gem_prime_unpin, .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table, .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table,
drivers/gpu/drm/nouveau/nouveau_gem.h +1 -0
@@ -35,6 +35,7 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *, struct drm_file *);extern int nouveau_gem_prime_pin (struct drm_gem_object *); +struct reservation_object *nouveau_gem_prime_res_obj (struct drm_gem_object *);extern void nouveau_gem_prime_unpin (struct drm_gem_object *);extern struct sg_table *nouveau_gem_prime_get_sg_table (struct drm_gem_object *); extern struct drm_gem_object *nouveau_gem_prime_import_sg_table(
drivers/gpu/drm/nouveau/nouveau_prime.c +7 -0
@@ -102,3 +102,10 @@ void nouveau_gem_prime_unpin(struct drm_gem_object *obj)nouveau_bo_unpin (nvbo); } + +struct reservation_object *nouveau_gem_prime_res_obj (struct drm_gem_object *obj) +{ +struct nouveau_bo *nvbo =nouveau_gem_object (obj); + +return nvbo->bo.resv; +}
drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +1 -1
@@ -171,7 +171,7 @@ static struct dma_buf_ops omap_dmabuf_ops = {struct dma_buf *omap_gem_prime_export (struct drm_device *dev,struct drm_gem_object *obj,int flags) { -return dma_buf_export (obj, &omap_dmabuf_ops, obj->size, flags); +return dma_buf_export (obj, &omap_dmabuf_ops, obj->size, flags,NULL ); } struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
drivers/gpu/drm/radeon/radeon_drv.c +2 -0
@@ -126,6 +126,7 @@ struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, struct sg_table *sg);int radeon_gem_prime_pin (struct drm_gem_object *obj);void radeon_gem_prime_unpin (struct drm_gem_object *obj); +struct reservation_object *radeon_gem_prime_res_obj (struct drm_gem_object *);void *radeon_gem_prime_vmap (struct drm_gem_object *obj);void radeon_gem_prime_vunmap (struct drm_gem_object *obj,void *vaddr); extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd,
@@ -549,6 +550,7 @@ static struct drm_driver kms_driver = { .gem_prime_import = drm_gem_prime_import, .gem_prime_pin = radeon_gem_prime_pin, .gem_prime_unpin = radeon_gem_prime_unpin, + .gem_prime_res_obj = radeon_gem_prime_res_obj, .gem_prime_get_sg_table = radeon_gem_prime_get_sg_table, .gem_prime_import_sg_table = radeon_gem_prime_import_sg_table, .gem_prime_vmap = radeon_gem_prime_vmap,
drivers/gpu/drm/radeon/radeon_prime.c +8 -0
@@ -103,3 +103,11 @@ void radeon_gem_prime_unpin(struct drm_gem_object *obj)radeon_bo_unpin (bo);radeon_bo_unreserve (bo); } + + +struct reservation_object *radeon_gem_prime_res_obj (struct drm_gem_object *obj) +{ +struct radeon_bo *bo =gem_to_radeon_bo (obj); + +return bo->tbo.resv; +}
drivers/gpu/drm/ttm/ttm_object.c +1 -1
@@ -631,7 +631,7 @@ int ttm_prime_handle_to_fd(struct ttm_object_file *tfile, } dma_buf =dma_buf_export (prime, &tdev->ops, - prime->size, flags); + prime->size, flags,NULL );if (IS_ERR (dma_buf)) { ret =PTR_ERR (dma_buf); ttm_mem_global_free(tdev->mem_glob,
drivers/media/v4l2-core/videobuf2-dma-contig.c +1 -1
@@ -404,7 +404,7 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags)if (WARN_ON (!buf->sgt_base))return NULL ; - dbuf =dma_buf_export (buf, &vb2_dc_dmabuf_ops, buf->size, flags); + dbuf =dma_buf_export (buf, &vb2_dc_dmabuf_ops, buf->size, flags,NULL );if (IS_ERR (dbuf))return NULL ;
include/drm/drmP.h +2 -0
@@ -81,6 +81,7 @@ struct drm_device;struct device_node ;struct videomode ; +struct reservation_object ;#include <drm/drm_os_linux.h> #include <drm/drm_hashtab.h>
@@ -950,6 +951,7 @@ struct drm_driver {/* low-level interface used by drm_gem_prime_{import,export} */ int (*gem_prime_pin)(struct drm_gem_object *obj);void (*gem_prime_unpin)(struct drm_gem_object *obj); +struct reservation_object *(*gem_prime_res_obj)(struct drm_gem_object *);struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj); struct drm_gem_object *(*gem_prime_import_sg_table)( struct drm_device *dev, size_t size,
include/linux/dma-buf.h +6 -3
@@ -115,6 +115,7 @@ struct dma_buf_ops {* @exp_name: name of the exporter; useful for debugging. * @list_node: node for dma_buf accounting and debugging. * @priv: exporter specific private data for this buffer object. +* @resv: reservation object linked to this dma-buf */ struct dma_buf {size_t size;
@@ -128,6 +129,7 @@ struct dma_buf {const char *exp_name;struct list_head list_node;void *priv; +struct reservation_object *resv; };/**
@@ -168,10 +170,11 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *dmabuf_attach);struct dma_buf *dma_buf_export_named (void *priv,const struct dma_buf_ops *ops, -size_t size,int flags,const char *); +size_t size,int flags,const char *, +struct reservation_object *); -#define dma_buf_export (priv, ops, size, flags) \ - dma_buf_export_named(priv, ops, size, flags, __FILE__) +#define dma_buf_export (priv, ops, size, flags, resv) \ + dma_buf_export_named(priv, ops, size, flags, __FILE__, resv)int dma_buf_fd (struct dma_buf *dmabuf,int flags);struct dma_buf *dma_buf_get (int fd);
Re: [PATCH 4/7] dma-buf: use reservation objects
Em Mon, 13 Jan 2014 13:32:16 +0100
Maarten Lankhorst <[email protected]> escreveu:
Quoted 17 lines
This allows reservation objects to be used in dma-buf. it's required for implementing polling support on the fences that belong to a dma-buf.
Signed-off-by Maarten Lankhorst <[email protected]>--- drivers/base/dma-buf.c | 22 ++++++++++++++++++++-- drivers/gpu/drm/drm_prime.c | 8 +++++++- drivers/gpu/drm/exynos/exynos_drm_dmabuf.c | 2 +- drivers/gpu/drm/i915/i915_gem_dmabuf.c | 2 +- drivers/gpu/drm/nouveau/nouveau_drm.c | 1 + drivers/gpu/drm/nouveau/nouveau_gem.h | 1 + drivers/gpu/drm/nouveau/nouveau_prime.c | 7 +++++++ drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c | 2 +- drivers/gpu/drm/radeon/radeon_drv.c | 2 ++ drivers/gpu/drm/radeon/radeon_prime.c | 8 ++++++++ drivers/gpu/drm/ttm/ttm_object.c | 2 +-
Quoted 1 line
drivers/media/v4l2-core/videobuf2-dma-contig.c | 2 +-
With regards to this small change on VB2-dma-contig:
Quoted 298 lines
include/drm/drmP.h | 2 ++ include/linux/dma-buf.h | 9 ++++++--- 14 files changed, 59 insertions(+), 11 deletions(-)drivers/base/dma-buf.c +20
-2@@ -25,10 +25,12 @@#include <linux/fs.h> #include <linux/slab.h> #include <linux/dma-buf.h> +#include <linux/fence.h> #include <linux/anon_inodes.h> #include <linux/export.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/reservation.h> static inline int is_dma_buf_file (struct file *);@@ -56,6 +58,9 @@ static int dma_buf_release(struct inode *inode, struct file *file)list_del (&dmabuf->list_node);mutex_unlock (&db_list.lock); +if (dmabuf->resv == (struct reservation_object *)&dmabuf[1 ]) +reservation_object_fini (dmabuf->resv); +kfree (dmabuf);return 0 ; }@@ -128,6 +133,7 @@ static inline int is_dma_buf_file(struct file *file)* @size: [in] Size of the buffer * @flags: [in] mode flags for the file. * @exp_name: [in] name of the exporting module - useful for debugging. +* @resv: [in] reservation-object, NULL to allocate default one. * * Returns, on success, a newly created dma_buf object, which wraps the * supplied private data and operations for dma_buf_ops. On either missing @@ -135,10 +141,17 @@ static inline int is_dma_buf_file(struct file *file)* */ struct dma_buf *dma_buf_export_named (void *priv,const struct dma_buf_ops *ops, -size_t size,int flags,const char *exp_name) +size_t size,int flags,const char *exp_name, +struct reservation_object *resv) {struct dma_buf *dmabuf;struct file *file; +size_t alloc_size =sizeof (struct dma_buf ); +if (!resv) + alloc_size +=sizeof (struct reservation_object ); +else +/* prevent &dma_buf[1] == dma_buf->resv */ + alloc_size +=1 ; if (WARN_ON(!priv || !ops || !ops->map_dma_buf@@ -150,7 +163,7 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,return ERR_PTR (-EINVAL ); } - dmabuf =kzalloc (sizeof (struct dma_buf ),GFP_KERNEL ); + dmabuf =kzalloc (alloc_size,GFP_KERNEL );if (dmabuf ==NULL )return ERR_PTR (-ENOMEM );@@ -158,6 +171,11 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, dmabuf->ops = ops; dmabuf->size = size; dmabuf->exp_name = exp_name; +if (!resv) { + resv = (struct reservation_object *)&dmabuf[1 ]; +reservation_object_init (resv); + } + dmabuf->resv = resv; file =anon_inode_getfile ("dmabuf" , &dma_buf_fops, dmabuf, flags);if (IS_ERR (file)) {drivers/gpu/drm/drm_prime.c +7
-1@@ -318,7 +318,13 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {struct dma_buf *drm_gem_prime_export (struct drm_device *dev,struct drm_gem_object *obj,int flags) { -return dma_buf_export (obj, &drm_gem_prime_dmabuf_ops, obj->size, flags); +struct reservation_object *robj =NULL ; + +if (dev->driver->gem_prime_res_obj) + robj = dev->driver->gem_prime_res_obj (obj); + +return dma_buf_export (obj, &drm_gem_prime_dmabuf_ops, obj->size, + flags, robj); }EXPORT_SYMBOL (drm_gem_prime_export);drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +1
-1@@ -187,7 +187,7 @@ struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev,struct exynos_drm_gem_obj *exynos_gem_obj =to_exynos_gem_obj (obj);return dma_buf_export (obj, &exynos_dmabuf_ops, - exynos_gem_obj->base.size, flags); + exynos_gem_obj->base.size, flags,NULL ); } struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,drivers/gpu/drm/i915/i915_gem_dmabuf.c +1
-1@@ -233,7 +233,7 @@ static const struct dma_buf_ops i915_dmabuf_ops = {struct dma_buf *i915_gem_prime_export (struct drm_device *dev,struct drm_gem_object *gem_obj,int flags) { -return dma_buf_export (gem_obj, &i915_dmabuf_ops, gem_obj->size, flags); +return dma_buf_export (gem_obj, &i915_dmabuf_ops, gem_obj->size, flags,NULL ); }static int i915_gem_object_get_pages_dmabuf (struct drm_i915_gem_object *obj)drivers/gpu/drm/nouveau/nouveau_drm.c +1
-0@@ -808,6 +808,7 @@ driver = { .gem_prime_export = drm_gem_prime_export, .gem_prime_import = drm_gem_prime_import, .gem_prime_pin = nouveau_gem_prime_pin, + .gem_prime_res_obj = nouveau_gem_prime_res_obj, .gem_prime_unpin = nouveau_gem_prime_unpin, .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table, .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table,drivers/gpu/drm/nouveau/nouveau_gem.h +1
-0@@ -35,6 +35,7 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *, struct drm_file *);extern int nouveau_gem_prime_pin (struct drm_gem_object *); +struct reservation_object *nouveau_gem_prime_res_obj (struct drm_gem_object *);extern void nouveau_gem_prime_unpin (struct drm_gem_object *);extern struct sg_table *nouveau_gem_prime_get_sg_table (struct drm_gem_object *); extern struct drm_gem_object *nouveau_gem_prime_import_sg_table(drivers/gpu/drm/nouveau/nouveau_prime.c +7
-0@@ -102,3 +102,10 @@ void nouveau_gem_prime_unpin(struct drm_gem_object *obj)nouveau_bo_unpin (nvbo); } + +struct reservation_object *nouveau_gem_prime_res_obj (struct drm_gem_object *obj) +{ +struct nouveau_bo *nvbo =nouveau_gem_object (obj); + +return nvbo->bo.resv; +}drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +1
-1@@ -171,7 +171,7 @@ static struct dma_buf_ops omap_dmabuf_ops = {struct dma_buf *omap_gem_prime_export (struct drm_device *dev,struct drm_gem_object *obj,int flags) { -return dma_buf_export (obj, &omap_dmabuf_ops, obj->size, flags); +return dma_buf_export (obj, &omap_dmabuf_ops, obj->size, flags,NULL ); } struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,drivers/gpu/drm/radeon/radeon_drv.c +2
-0@@ -126,6 +126,7 @@ struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, struct sg_table *sg);int radeon_gem_prime_pin (struct drm_gem_object *obj);void radeon_gem_prime_unpin (struct drm_gem_object *obj); +struct reservation_object *radeon_gem_prime_res_obj (struct drm_gem_object *);void *radeon_gem_prime_vmap (struct drm_gem_object *obj);void radeon_gem_prime_vunmap (struct drm_gem_object *obj,void *vaddr); extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd,@@ -549,6 +550,7 @@ static struct drm_driver kms_driver = { .gem_prime_import = drm_gem_prime_import, .gem_prime_pin = radeon_gem_prime_pin, .gem_prime_unpin = radeon_gem_prime_unpin, + .gem_prime_res_obj = radeon_gem_prime_res_obj, .gem_prime_get_sg_table = radeon_gem_prime_get_sg_table, .gem_prime_import_sg_table = radeon_gem_prime_import_sg_table, .gem_prime_vmap = radeon_gem_prime_vmap,drivers/gpu/drm/radeon/radeon_prime.c +8
-0@@ -103,3 +103,11 @@ void radeon_gem_prime_unpin(struct drm_gem_object *obj)radeon_bo_unpin (bo);radeon_bo_unreserve (bo); } + + +struct reservation_object *radeon_gem_prime_res_obj (struct drm_gem_object *obj) +{ +struct radeon_bo *bo =gem_to_radeon_bo (obj); + +return bo->tbo.resv; +}drivers/gpu/drm/ttm/ttm_object.c +1
-1@@ -631,7 +631,7 @@ int ttm_prime_handle_to_fd(struct ttm_object_file *tfile, } dma_buf =dma_buf_export (prime, &tdev->ops, - prime->size, flags); + prime->size, flags,NULL );if (IS_ERR (dma_buf)) { ret =PTR_ERR (dma_buf); ttm_mem_global_free(tdev->mem_glob,drivers/media/v4l2-core/videobuf2-dma-contig.c +1
-1@@ -404,7 +404,7 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags)if (WARN_ON (!buf->sgt_base))return NULL ; - dbuf =dma_buf_export (buf, &vb2_dc_dmabuf_ops, buf->size, flags); + dbuf =dma_buf_export (buf, &vb2_dc_dmabuf_ops, buf->size, flags,NULL );if (IS_ERR (dbuf))return NULL ;include/drm/drmP.h +2
-0@@ -81,6 +81,7 @@ struct drm_device;struct device_node ;struct videomode ; +struct reservation_object ;#include <drm/drm_os_linux.h> #include <drm/drm_hashtab.h> @@ -950,6 +951,7 @@ struct drm_driver {/* low-level interface used by drm_gem_prime_{import,export} */ int (*gem_prime_pin)(struct drm_gem_object *obj);void (*gem_prime_unpin)(struct drm_gem_object *obj); +struct reservation_object *(*gem_prime_res_obj)(struct drm_gem_object *);struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj); struct drm_gem_object *(*gem_prime_import_sg_table)( struct drm_device *dev, size_t size,include/linux/dma-buf.h +6
-4@@ -115,6 +115,7 @@ struct dma_buf_ops {* @exp_name: name of the exporter; useful for debugging. * @list_node: node for dma_buf accounting and debugging. * @priv: exporter specific private data for this buffer object. +* @resv: reservation object linked to this dma-buf */ struct dma_buf {size_t size;@@ -128,6 +129,7 @@ struct dma_buf {const char *exp_name;struct list_head list_node;void *priv; +struct reservation_object *resv; };/** @@ -168,10 +170,11 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *dmabuf_attach);struct dma_buf *dma_buf_export_named (void *priv,const struct dma_buf_ops *ops, -size_t size,int flags,const char *); +size_t size,int flags,const char *, +struct reservation_object *); -#define dma_buf_export (priv, ops, size, flags) \ - dma_buf_export_named(priv, ops, size, flags, __FILE__) +#define dma_buf_export (priv, ops, size, flags, resv) \ + dma_buf_export_named(priv, ops, size, flags, __FILE__, resv)int dma_buf_fd (struct dma_buf *dmabuf,int flags);struct dma_buf *dma_buf_get (int fd); --To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 2 lines
Cheers, Mauro
PATCH 5/7 android: convert sync to fence api, v3
In reply to Maarten LankhorstAndroid syncpoints can be mapped to a timeline. This removes the need to maintain a separate api for synchronization. I've left the android trace events in place, but the core fence events should already be sufficient for debugging.
v2:
- Call fence_remove_callback in sync_fence_free if not all fences have fired. v3:
- Merge Colin Cross' bugfixes, and the android fence merge optimization.
--- drivers/staging/android/Kconfig | 1 drivers/staging/android/Makefile | 2 drivers/staging/android/sw_sync.c | 4 drivers/staging/android/sync.c | 895 +++++++++++----------------------- drivers/staging/android/sync.h | 85 ++- drivers/staging/android/sync_debug.c | 245 +++++++++ drivers/staging/android/trace/sync.h | 12 7 files changed, 592 insertions(+), 652 deletions(-) create mode 100644 drivers/staging/android/sync_debug.c
drivers/staging/android/Kconfig +1 -0
@@ -77,6 +77,7 @@ config SYNC bool "Synchronization framework" default n select ANON_INODES + select DMA_SHARED_BUFFER ---help--- This option enables the framework for synchronization between multiple drivers. Sync implementations can take advantage of hardware
drivers/staging/android/Makefile +1 -1
@@ -7,5 +7,5 @@ obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o -obj-$(CONFIG_SYNC) += sync.o +obj-$(CONFIG_SYNC) += sync.o sync_debug.o obj-$(CONFIG_SW_SYNC) += sw_sync.o
drivers/staging/android/sw_sync.c +2 -2
@@ -50,7 +50,7 @@ static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt) {struct sw_sync_pt *pt = (struct sw_sync_pt *) sync_pt;struct sw_sync_timeline *obj = - (struct sw_sync_timeline *)sync_pt->parent; + (struct sw_sync_timeline *)sync_pt_parent (sync_pt);return (struct sync_pt *)sw_sync_pt_create (obj, pt->value); }
@@ -59,7 +59,7 @@ static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt) {struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;struct sw_sync_timeline *obj = - (struct sw_sync_timeline *)sync_pt->parent; + (struct sw_sync_timeline *)sync_pt_parent (sync_pt);return sw_sync_cmp (obj->value, pt->value) >=0 ; }
drivers/staging/android/sync.c +293 -602
1,128 lines not on this page · raw message
drivers/staging/android/sync.h +44 -41
164 lines not on this page · raw message
drivers/staging/android/sync_debug.c new file +245 -0
245 lines not on this page · raw message
drivers/staging/android/trace/sync.h +6 -6
@@ -45,7 +45,7 @@ TRACE_EVENT(sync_wait,TP_fast_assign ( __assign_str(name, fence->name); - __entry->status = fence->status; + __entry->status =atomic_read (&fence->status); __entry->begin = begin; ),
@@ -54,19 +54,19 @@ TRACE_EVENT(sync_wait, ); TRACE_EVENT(sync_pt, - TP_PROTO(struct sync_pt *pt), + TP_PROTO(struct fence *pt), TP_ARGS(pt), TP_STRUCT__entry( - __string(timeline, pt->parent->name) + __string(timeline, pt->ops->get_timeline_name(pt)) __array(char, value, 32) ), TP_fast_assign( - __assign_str(timeline, pt->parent->name); - if (pt->parent->ops->pt_value_str) { - pt->parent->ops->pt_value_str(pt, __entry->value, + __assign_str(timeline, pt->ops->get_timeline_name(pt)); + if (pt->ops->fence_value_str) { + pt->ops->fence_value_str(pt, __entry->value, sizeof(__entry->value)); } else { __entry->value[0] = '\0';
PATCH 6/7 reservation: add support for fences to enable cross-device synchronisation
In reply to Maarten Lankhorst--- include/linux/reservation.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
include/linux/reservation.h +17 -1
@@ -6,7 +6,7 @@* Copyright (C) 2012 Texas Instruments * * Authors: -* Rob Clark <[email protected]> +* Rob Clark <[email protected]> * Maarten Lankhorst <[email protected]> * Thomas Hellstrom <thellstrom-at-vmware-dot-com> *
@@ -40,22 +40,38 @@#define _LINUX_RESERVATION_H#include <linux/ww_mutex.h> +#include <linux/fence.h> extern struct ww_class reservation_ww_class;struct reservation_object {struct ww_mutex lock; + +struct fence *fence_excl; +struct fence **fence_shared; +u32 fence_shared_count, fence_shared_max; };static inline void reservation_object_init (struct reservation_object *obj) {ww_mutex_init (&obj->lock, &reservation_ww_class); + + obj->fence_shared_count = obj->fence_shared_max =0 ; + obj->fence_shared =NULL ; + obj->fence_excl =NULL ; }static inline void reservation_object_fini (struct reservation_object *obj) { +int i; + +if (obj->fence_excl) +fence_put (obj->fence_excl); +for (i =0 ; i < obj->fence_shared_count; ++i) +fence_put (obj->fence_shared[i]); +ww_mutex_destroy (&obj->lock); }
PATCH 7/7 dma-buf: add poll support
In reply to Maarten LankhorstThanks to Fengguang Wu for spotting a missing static cast.
--- drivers/base/dma-buf.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/dma-buf.h | 12 ++++++ 2 files changed, 114 insertions(+)
drivers/base/dma-buf.c +102 -0
@@ -30,6 +30,7 @@#include <linux/export.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/poll.h> #include <linux/reservation.h> static inline int is_dma_buf_file (struct file *);
@@ -52,6 +53,13 @@ static int dma_buf_release(struct inode *inode, struct file *file)BUG_ON (dmabuf->vmapping_counter); +/* +* Any fences that a dma-buf poll can wait on should be signaled +* before releasing dma-buf. This is the responsibility of each +* driver that uses the reservation objects. +*/ +BUG_ON (dmabuf->cb_shared.active || dmabuf->cb_excl.active); + dmabuf->ops->release (dmabuf);mutex_lock (&db_list.lock);
@@ -108,10 +116,100 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence)return base + offset; } +static void dma_buf_poll_cb (struct fence *fence,struct fence_cb *cb) +{ +struct dma_buf_poll_cb_t *dcb = (struct dma_buf_poll_cb_t *) cb; +unsigned long flags; + +spin_lock_irqsave (&dcb->poll->lock, flags); +wake_up_locked_poll (dcb->poll, dcb->active); + dcb->active =0 ; +spin_unlock_irqrestore (&dcb->poll->lock, flags); +} + +static unsigned int dma_buf_poll (struct file *file,poll_table *poll) +{ +struct dma_buf *dmabuf; +struct reservation_object *resv; +unsigned long events; +int need_shared; + + dmabuf = file->private_data; +if (!dmabuf || !dmabuf->resv) +return POLLERR ; + + resv = dmabuf->resv; + +poll_wait (file, &dmabuf->poll, poll); + + events =poll_requested_events (poll) & (POLLIN |POLLOUT ); +if (!events) +return 0 ; + +ww_mutex_lock (&resv->lock,NULL ); + +if (resv->fence_excl && (!(events &POLLOUT ) || resv->fence_shared_count ==0 )) { +struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_excl; +unsigned long pevents =POLLIN ; + +if (resv->fence_shared_count ==0 ) + pevents |=POLLOUT ; + +spin_lock_irq (&dmabuf->poll.lock); +if (dcb->active) { + dcb->active |= pevents; + events &= ~pevents; + }else + dcb->active = pevents; +spin_unlock_irq (&dmabuf->poll.lock); + +if (events & pevents) { +if (!fence_add_callback (resv->fence_excl, + &dcb->cb, dma_buf_poll_cb)) + events &= ~pevents; +else +// No callback queued, wake up any additional waiters. +dma_buf_poll_cb (NULL , &dcb->cb); + } + } + +if ((events &POLLOUT ) && resv->fence_shared_count >0 ) { +struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_shared; +int i; + +/* Only queue a new callback if no event has fired yet */ +spin_lock_irq (&dmabuf->poll.lock); +if (dcb->active) + events &= ~POLLOUT ; +else + dcb->active =POLLOUT ; +spin_unlock_irq (&dmabuf->poll.lock); + +if (!(events &POLLOUT )) + goto out; + +for (i =0 ; i < resv->fence_shared_count; ++i) +if (!fence_add_callback (resv->fence_shared[i], + &dcb->cb, dma_buf_poll_cb)) { + events &= ~POLLOUT ; +break ; + } + +// No callback queued, wake up any additional waiters. +if (i == resv->fence_shared_count) +dma_buf_poll_cb (NULL , &dcb->cb); + } + +out: +ww_mutex_unlock (&resv->lock); +return events; +} +static const struct file_operations dma_buf_fops = { .release = dma_buf_release, .mmap = dma_buf_mmap_internal, .llseek = dma_buf_llseek, + .poll = dma_buf_poll, };/*
@@ -171,6 +269,10 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, dmabuf->ops = ops; dmabuf->size = size; dmabuf->exp_name = exp_name; +init_waitqueue_head (&dmabuf->poll); + dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll; + dmabuf->cb_excl.active = dmabuf->cb_shared.active =0 ; +if (!resv) { resv = (struct reservation_object *)&dmabuf[1 ];reservation_object_init (resv);
include/linux/dma-buf.h +12 -0
@@ -30,6 +30,8 @@#include <linux/list.h> #include <linux/dma-mapping.h> #include <linux/fs.h> +#include <linux/fence.h> +#include <linux/wait.h> struct device ;struct dma_buf ;
@@ -130,6 +132,16 @@ struct dma_buf {struct list_head list_node;void *priv;struct reservation_object *resv; + +/* poll support */ +wait_queue_head_t poll; + +struct dma_buf_poll_cb_t { +struct fence_cb cb; +wait_queue_head_t *poll; + +unsigned long active; + } cb_excl, cb_shared; };/**