new file mode 100644
@@ -0,0 +1,75 @@
+Motivation:
+
+In complicated DMA pipelines such as graphics (multimedia, camera, gpu, display)
+a consumer of a buffer needs to know when the producer has finished producing
+it. Likewise the producer needs to know when the consumer is finished with the
+buffer so it can reuse it. A particular buffer may be consumed by multiple
+consumers which will retain the buffer for different amounts of time. In
+addition, a consumer may consume multiple buffers atomically.
+The sync framework adds an API which allows synchronization between the
+producers and consumers in a generic way while also allowing platforms which
+have shared hardware synchronization primitives to exploit them.
+
+Goals:
+ * provide a generic API for expressing synchronization dependencies
+ * allow drivers to exploit hardware synchronization between hardware
+ blocks
+ * provide a userspace API that allows a compositor to manage
+ dependencies.
+ * provide rich telemetry data to allow debugging slowdowns and stalls of
+ the graphics pipeline.
+
+Objects:
+ * sync_timeline
+ * sync_pt
+ * sync_fence
+
+sync_timeline:
+
+A sync_timeline is an abstract monotonically increasing counter. In general,
+each driver/hardware block context will have one of these. They can be backed
+by the appropriate hardware or rely on the generic sw_sync implementation.
+Timelines are only ever created through their specific implementations
+(i.e. sw_sync.)
+
+sync_pt:
+
+A sync_pt is an abstract value which marks a point on a sync_timeline. Sync_pts
+have a single timeline parent. They have 3 states: active, signaled, and error.
+They start in active state and transition, once, to either signaled (when the
+timeline counter advances beyond the sync_pt’s value) or error state.
+
+sync_fence:
+
+Sync_fences are the primary primitives used by drivers to coordinate
+synchronization of their buffers. They are a collection of sync_pts which may
+or may not have the same timeline parent. A sync_pt can only exist in one fence
+and the fence's list of sync_pts is immutable once created. Fences can be
+waited on synchronously or asynchronously. Two fences can also be merged to
+create a third fence containing a copy of the two fences’ sync_pts. Fences are
+backed by file descriptors to allow userspace to coordinate the display pipeline
+dependencies.
+
+Use:
+
+A driver implementing sync support should have a work submission function which:
+ * takes a fence argument specifying when to begin work
+ * asynchronously queues that work to kick off when the fence is signaled
+ * returns a fence to indicate when its work will be done.
+ * signals the returned fence once the work is completed.
+
+Consider an imaginary display driver that has the following API:
+/*
+ * assumes buf is ready to be displayed.
+ * blocks until the buffer is on screen.
+ */
+ void display_buffer(struct dma_buf *buf);
+
+The new API will become:
+/*
+ * will display buf when fence is signaled.
+ * returns immediately with a fence that will signal when buf
+ * is no longer displayed.
+ */
+struct sync_fence* display_buffer(struct dma_buf *buf,
+ struct sync_fence *fence);
@@ -192,4 +192,12 @@ config DMA_SHARED_BUFFER
APIs extension; the file's descriptor can then be passed on to other
driver.
+config SYNC
+ bool "Synchronization framework"
+ default n
+ select ANON_INODES
+ help
+ This option enables the framework for synchronization between multiple
+ drivers. Sync implementations can take advantage of hardware
+ synchronization built into devices like GPUs.
endmenu
@@ -21,5 +21,7 @@ obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
obj-$(CONFIG_REGMAP) += regmap/
obj-$(CONFIG_SOC_BUS) += soc.o
+obj-$(CONFIG_SYNC) += sync.o
+
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
new file mode 100644
@@ -0,0 +1,525 @@
+/*
+ * drivers/base/sync.c
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/sync.h>
+#include <linux/uaccess.h>
+
+#include <linux/anon_inodes.h>
+
+static void sync_fence_signal_pt(struct sync_pt *pt);
+static int _sync_pt_has_signaled(struct sync_pt *pt);
+
+struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
+ int size, const char *name)
+{
+ struct sync_timeline *obj;
+
+ if (size < sizeof(struct sync_timeline))
+ return NULL;
+
+ obj = kzalloc(size, GFP_KERNEL);
+ if (obj == NULL)
+ return NULL;
+
+ obj->ops = ops;
+ strlcpy(obj->name, name, sizeof(obj->name));
+
+ INIT_LIST_HEAD(&obj->child_list_head);
+ spin_lock_init(&obj->child_list_lock);
+
+ INIT_LIST_HEAD(&obj->active_list_head);
+ spin_lock_init(&obj->active_list_lock);
+
+ return obj;
+}
+
+void sync_timeline_destroy(struct sync_timeline *obj)
+{
+ unsigned long flags;
+ bool needs_freeing;
+
+ spin_lock_irqsave(&obj->child_list_lock, flags);
+ obj->destroyed = true;
+ needs_freeing = list_empty(&obj->child_list_head);
+ spin_unlock_irqrestore(&obj->child_list_lock, flags);
+
+ if (needs_freeing)
+ kfree(obj);
+ else
+ sync_timeline_signal(obj);
+}
+
+static void sync_timeline_add_pt(struct sync_timeline *obj, struct sync_pt *pt)
+{
+ unsigned long flags;
+
+ pt->parent = obj;
+
+ spin_lock_irqsave(&obj->child_list_lock, flags);
+ list_add_tail(&pt->child_list, &obj->child_list_head);
+ spin_unlock_irqrestore(&obj->child_list_lock, flags);
+}
+
+static void sync_timeline_remove_pt(struct sync_pt *pt)
+{
+ struct sync_timeline *obj = pt->parent;
+ unsigned long flags;
+ bool needs_freeing;
+
+ spin_lock_irqsave(&obj->active_list_lock, flags);
+ if (!list_empty(&pt->active_list))
+ list_del_init(&pt->active_list);
+ spin_unlock_irqrestore(&obj->active_list_lock, flags);
+
+ spin_lock_irqsave(&obj->child_list_lock, flags);
+ list_del(&pt->child_list);
+ needs_freeing = obj->destroyed && list_empty(&obj->child_list_head);
+ spin_unlock_irqrestore(&obj->child_list_lock, flags);
+
+ if (needs_freeing)
+ kfree(obj);
+}
+
+void sync_timeline_signal(struct sync_timeline *obj)
+{
+ unsigned long flags;
+ LIST_HEAD(signaled_pts);
+ struct list_head *pos, *n;
+
+ spin_lock_irqsave(&obj->active_list_lock, flags);
+
+ list_for_each_safe(pos, n, &obj->active_list_head) {
+ struct sync_pt *pt =
+ container_of(pos, struct sync_pt, active_list);
+
+ if (_sync_pt_has_signaled(pt))
+ list_move(pos, &signaled_pts);
+ }
+
+ spin_unlock_irqrestore(&obj->active_list_lock, flags);
+
+ list_for_each_safe(pos, n, &signaled_pts) {
+ struct sync_pt *pt =
+ container_of(pos, struct sync_pt, active_list);
+
+ list_del_init(pos);
+ sync_fence_signal_pt(pt);
+ }
+}
+
+struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size)
+{
+ struct sync_pt *pt;
+
+ if (size < sizeof(struct sync_pt))
+ return NULL;
+
+ pt = kzalloc(size, GFP_KERNEL);
+ if (pt == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&pt->active_list);
+ sync_timeline_add_pt(parent, pt);
+
+ return pt;
+}
+
+void sync_pt_free(struct sync_pt *pt)
+{
+ if (pt->parent->ops->free_pt)
+ pt->parent->ops->free_pt(pt);
+
+ sync_timeline_remove_pt(pt);
+
+ kfree(pt);
+}
+
+/* call with pt->parent->active_list_lock held */
+static int _sync_pt_has_signaled(struct sync_pt *pt)
+{
+ if (!pt->status)
+ pt->status = pt->parent->ops->has_signaled(pt);
+
+ if (!pt->status && pt->parent->destroyed)
+ pt->status = -ENOENT;
+
+ return pt->status;
+}
+
+static struct sync_pt *sync_pt_dup(struct sync_pt *pt)
+{
+ return pt->parent->ops->dup(pt);
+}
+
+/* Adds a sync pt to the active queue. Called when added to a fence */
+static void sync_pt_activate(struct sync_pt *pt)
+{
+ struct sync_timeline *obj = pt->parent;
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&obj->active_list_lock, flags);
+
+ err = _sync_pt_has_signaled(pt);
+ if (err != 0)
+ goto out;
+
+ list_add_tail(&pt->active_list, &obj->active_list_head);
+
+out:
+ spin_unlock_irqrestore(&obj->active_list_lock, flags);
+}
+
+static int sync_fence_release(struct inode *inode, struct file *file);
+static long sync_fence_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+
+
+static const struct file_operations sync_fence_fops = {
+ .release = sync_fence_release,
+ .unlocked_ioctl = sync_fence_ioctl,
+};
+
+static struct sync_fence *sync_fence_alloc(const char *name)
+{
+ struct sync_fence *fence;
+
+ fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL);
+ if (fence == NULL)
+ return NULL;
+
+ fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops,
+ fence, 0);
+ if (fence->file == NULL)
+ goto err;
+
+ strlcpy(fence->name, name, sizeof(fence->name));
+
+ INIT_LIST_HEAD(&fence->pt_list_head);
+ INIT_LIST_HEAD(&fence->waiter_list_head);
+ spin_lock_init(&fence->waiter_list_lock);
+
+ init_waitqueue_head(&fence->wq);
+ return fence;
+
+err:
+ kfree(fence);
+ return NULL;
+}
+
+/* TODO: implement a create which takes more that one sync_pt */
+struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)
+{
+ struct sync_fence *fence;
+
+ if (pt->fence)
+ return NULL;
+
+ fence = sync_fence_alloc(name);
+ if (fence == NULL)
+ return NULL;
+
+ pt->fence = fence;
+ list_add(&pt->pt_list, &fence->pt_list_head);
+ sync_pt_activate(pt);
+
+ return fence;
+}
+
+static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src)
+{
+ struct list_head *pos;
+
+ list_for_each(pos, &src->pt_list_head) {
+ struct sync_pt *orig_pt =
+ container_of(pos, struct sync_pt, pt_list);
+ struct sync_pt *new_pt = sync_pt_dup(orig_pt);
+
+ if (new_pt == NULL)
+ return -ENOMEM;
+
+ new_pt->fence = dst;
+ list_add(&new_pt->pt_list, &dst->pt_list_head);
+ sync_pt_activate(new_pt);
+ }
+
+ return 0;
+}
+
+static void sync_fence_free_pts(struct sync_fence *fence)
+{
+ struct list_head *pos, *n;
+
+ list_for_each_safe(pos, n, &fence->pt_list_head) {
+ struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
+ sync_pt_free(pt);
+ }
+}
+
+struct sync_fence *sync_fence_fdget(int fd)
+{
+ struct file *file = fget(fd);
+
+ if (file == NULL)
+ return NULL;
+
+ if (file->f_op != &sync_fence_fops)
+ goto err;
+
+ return file->private_data;
+
+err:
+ fput(file);
+ return NULL;
+}
+
+void sync_fence_put(struct sync_fence *fence)
+{
+ fput(fence->file);
+}
+
+void sync_fence_install(struct sync_fence *fence, int fd)
+{
+ fd_install(fd, fence->file);
+}
+
+static int sync_fence_get_status(struct sync_fence *fence)
+{
+ struct list_head *pos;
+ int status = 1;
+
+ list_for_each(pos, &fence->pt_list_head) {
+ struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
+ int pt_status = pt->status;
+
+ if (pt_status < 0) {
+ status = pt_status;
+ break;
+ } else if (status == 1) {
+ status = pt_status;
+ }
+ }
+
+ return status;
+}
+
+struct sync_fence *sync_fence_merge(const char *name,
+ struct sync_fence *a, struct sync_fence *b)
+{
+ struct sync_fence *fence;
+ int err;
+
+ fence = sync_fence_alloc(name);
+ if (fence == NULL)
+ return NULL;
+
+ err = sync_fence_copy_pts(fence, a);
+ if (err < 0)
+ goto err;
+
+ err = sync_fence_copy_pts(fence, b);
+ if (err < 0)
+ goto err;
+
+ fence->status = sync_fence_get_status(fence);
+
+ return fence;
+err:
+ sync_fence_free_pts(fence);
+ kfree(fence);
+ return NULL;
+}
+
+static void sync_fence_signal_pt(struct sync_pt *pt)
+{
+ LIST_HEAD(signaled_waiters);
+ struct sync_fence *fence = pt->fence;
+ struct list_head *pos;
+ struct list_head *n;
+ unsigned long flags;
+ int status;
+
+ status = sync_fence_get_status(fence);
+
+ spin_lock_irqsave(&fence->waiter_list_lock, flags);
+ /*
+ * this should protect against two threads racing on the signaled
+ * false -> true transition
+ */
+ if (status && !fence->status) {
+ list_for_each_safe(pos, n, &fence->waiter_list_head)
+ list_move(pos, &signaled_waiters);
+
+ fence->status = status;
+ } else {
+ status = 0;
+ }
+ spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
+
+ if (status) {
+ list_for_each_safe(pos, n, &signaled_waiters) {
+ struct sync_fence_waiter *waiter =
+ container_of(pos, struct sync_fence_waiter,
+ waiter_list);
+
+ waiter->callback(fence, waiter->callback_data);
+ list_del(pos);
+ kfree(waiter);
+ }
+ wake_up(&fence->wq);
+ }
+}
+
+int sync_fence_wait_async(struct sync_fence *fence,
+ void (*callback)(struct sync_fence *, void *data),
+ void *callback_data)
+{
+ struct sync_fence_waiter *waiter;
+ unsigned long flags;
+ int err = 0;
+
+ waiter = kzalloc(sizeof(struct sync_fence_waiter), GFP_KERNEL);
+ if (waiter == NULL)
+ return -ENOMEM;
+
+ waiter->callback = callback;
+ waiter->callback_data = callback_data;
+
+ spin_lock_irqsave(&fence->waiter_list_lock, flags);
+
+ if (fence->status) {
+ kfree(waiter);
+ err = fence->status;
+ goto out;
+ }
+
+ list_add_tail(&waiter->waiter_list, &fence->waiter_list_head);
+out:
+ spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
+
+ return err;
+}
+
+int sync_fence_wait(struct sync_fence *fence, long timeout)
+{
+ int err;
+
+ if (timeout) {
+ timeout = msecs_to_jiffies(timeout);
+ err = wait_event_interruptible_timeout(fence->wq,
+ fence->status != 0,
+ timeout);
+ } else {
+ err = wait_event_interruptible(fence->wq, fence->status != 0);
+ }
+
+ if (err < 0)
+ return err;
+
+ if (fence->status < 0)
+ return fence->status;
+
+ if (fence->status == 0)
+ return -ETIME;
+
+ return 0;
+}
+
+static int sync_fence_release(struct inode *inode, struct file *file)
+{
+ struct sync_fence *fence = file->private_data;
+
+ sync_fence_free_pts(fence);
+ kfree(fence);
+
+ return 0;
+}
+
+static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg)
+{
+ __u32 value;
+
+ if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
+ return -EFAULT;
+
+ return sync_fence_wait(fence, value);
+}
+
+static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg)
+{
+ int fd = get_unused_fd();
+ int err;
+ struct sync_fence *fence2, *fence3;
+ struct sync_merge_data data;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
+ return -EFAULT;
+
+ fence2 = sync_fence_fdget(data.fd2);
+ if (fence2 == NULL) {
+ err = -ENOENT;
+ goto err_put_fd;
+ }
+
+ data.name[sizeof(data.name) - 1] = '\0';
+ fence3 = sync_fence_merge(data.name, fence, fence2);
+ if (fence3 == NULL) {
+ err = -ENOMEM;
+ goto err_put_fence2;
+ }
+
+ data.fence = fd;
+ if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
+ err = -EFAULT;
+ goto err_put_fence3;
+ }
+
+ sync_fence_install(fence3, fd);
+ sync_fence_put(fence2);
+ return 0;
+
+err_put_fence3:
+ sync_fence_put(fence3);
+
+err_put_fence2:
+ sync_fence_put(fence2);
+
+err_put_fd:
+ put_unused_fd(fd);
+ return err;
+}
+
+
+static long sync_fence_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sync_fence *fence = file->private_data;
+ switch (cmd) {
+ case SYNC_IOC_WAIT:
+ return sync_fence_ioctl_wait(fence, arg);
+
+ case SYNC_IOC_MERGE:
+ return sync_fence_ioctl_merge(fence, arg);
+ default:
+ return -ENOTTY;
+ }
+}
+
new file mode 100644
@@ -0,0 +1,316 @@
+/*
+ * include/linux/sync.h
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_SYNC_H
+#define _LINUX_SYNC_H
+
+#include <linux/types.h>
+#ifdef __KERNEL__
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+struct sync_timeline;
+struct sync_pt;
+struct sync_fence;
+
+/**
+ * struct sync_timeline_ops - sync object implementation ops
+ * @driver_name: name of the implentation
+ * @dup: duplicate a sync_pt
+ * @has_signaled: returns:
+ * 1 if pt has signaled
+ * 0 if pt has not signaled
+ * <0 on error
+ * must be callable from atomic context
+ * @compare: returns:
+ * 1 if b will signal before a
+ * 0 if a and b will signal at the same time
+ * -1 if a will signabl before b
+ * must be callable from atomic context
+ * @free_pt: called before sync_pt is freed
+ * @release_obj: called before sync_timeline is freed
+ */
+struct sync_timeline_ops {
+ const char *driver_name;
+
+ /* required */
+ struct sync_pt *(*dup)(struct sync_pt *pt);
+
+ /* required */
+ int (*has_signaled)(struct sync_pt *pt);
+
+ /* required */
+ int (*compare)(struct sync_pt *a, struct sync_pt *b);
+
+ /* optional */
+ void (*free_pt)(struct sync_pt *sync_pt);
+
+ /* optional */
+ void (*release_obj)(struct sync_timeline *sync_timeline);
+};
+
+/**
+ * struct sync_timeline - sync object
+ * @ops: ops that define the implementaiton of the sync_timeline
+ * @name: name of the sync_timeline. Useful for debugging
+ * @destoryed: set when sync_timeline is destroyed
+ * @child_list_head: list of children sync_pts for this sync_timeline
+ * @child_list_lock: lock protecting @child_list_head, destroyed, and
+ * sync_pt.status
+ * @active_list_head: list of active (unsignaled/errored) sync_pts
+ */
+struct sync_timeline {
+ const struct sync_timeline_ops *ops;
+ char name[32];
+
+ /* protected by child_list_lock */
+ bool destroyed;
+
+ struct list_head child_list_head;
+ spinlock_t child_list_lock;
+
+ struct list_head active_list_head;
+ spinlock_t active_list_lock;
+};
+
+/**
+ * struct sync_pt - sync point
+ * @parent: sync_timeline to which this sync_pt belongs
+ * @child_list: membership in sync_timeline.child_list_head
+ * @active_list: membership in sync_timeline.active_list_head
+ * @fence: sync_fence to which the sync_pt belongs
+ * @pt_list: membership in sync_fence.pt_list_head
+ * @status: 1: signaled, 0:active, <0: error
+ */
+struct sync_pt {
+ struct sync_timeline *parent;
+ struct list_head child_list;
+
+ struct list_head active_list;
+
+ struct sync_fence *fence;
+ struct list_head pt_list;
+
+ /* protected by parent->active_list_lock */
+ int status;
+};
+
+/**
+ * struct sync_fence - sync fence
+ * @file: file representing this fence
+ * @name: name of sync_fence. Useful for debugging
+ * @pt_list_head: list of sync_pts in ths fence. immutable once fence
+ * is created
+ * @waiter_list_head: list of asynchronous waiters on this fence
+ * @waiter_list_lock: lock protecting @waiter_list_head and @status
+ * @status: 1: signaled, 0:active, <0: error
+ *
+ * @wq: wait queue for fence signaling
+ */
+struct sync_fence {
+ struct file *file;
+ char name[32];
+
+ /* this list is immutable once the fence is created */
+ struct list_head pt_list_head;
+
+ struct list_head waiter_list_head;
+ spinlock_t waiter_list_lock; /* also protects status */
+ int status;
+
+ wait_queue_head_t wq;
+};
+
+/**
+ * struct sync_fence_waiter - metadata for asynchronous waiter on a fence
+ * @waiter_list: membership in sync_fence.waiter_list_head
+ * @callback: function pointer to call when fence signals
+ * @callback_data: pointer to pass to @callback
+ */
+struct sync_fence_waiter {
+ struct list_head waiter_list;
+
+ void (*callback)(struct sync_fence *fence, void *data);
+ void *callback_data;
+};
+
+/*
+ * API for sync_timeline implementers
+ */
+
+/**
+ * sync_timeline_create() - creates a sync object
+ * @ops: specifies the implemention ops for the object
+ * @size: size to allocate for this obj
+ * @name: sync_timeline name
+ *
+ * Creates a new sync_timeline which will use the implemetation specified by
+ * @ops. @size bytes will be allocated allowing for implemntation specific
+ * data to be kept after the generic sync_timeline stuct.
+ */
+struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
+ int size, const char *name);
+
+/**
+ * sync_timeline_destory() - destorys a sync object
+ * @obj: sync_timeline to destroy
+ *
+ * A sync implemntation should call this when the @obj is going away
+ * (i.e. module unload.) @obj won't actually be freed until all its childern
+ * sync_pts are freed.
+ */
+void sync_timeline_destroy(struct sync_timeline *obj);
+
+/**
+ * sync_timeline_signal() - signal a status change on a sync_timeline
+ * @obj: sync_timeline to signal
+ *
+ * A sync implemntation should call this any time one of it's sync_pts
+ * has signaled or has an error condition.
+ */
+void sync_timeline_signal(struct sync_timeline *obj);
+
+/**
+ * sync_pt_create() - creates a sync pt
+ * @parent: sync_pt's parent sync_timeline
+ * @size: size to allocate for this pt
+ *
+ * Creates a new sync_pt as a chiled of @parent. @size bytes will be
+ * allocated allowing for implemntation specific data to be kept after
+ * the generic sync_timeline struct.
+ */
+struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size);
+
+/**
+ * sync_pt_free() - frees a sync pt
+ * @pt: sync_pt to free
+ *
+ * This should only be called on sync_pts which have been created but
+ * not added to a fence.
+ */
+void sync_pt_free(struct sync_pt *pt);
+
+/**
+ * sync_fence_create() - creates a sync fence
+ * @name: name of fence to create
+ * @pt: sync_pt to add to the fence
+ *
+ * Creates a fence containg @pt. Once this is called, the fence takes
+ * ownership of @pt.
+ */
+struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt);
+
+/*
+ * API for sync_fence consumers
+ */
+
+/**
+ * sync_fence_merge() - merge two fences
+ * @name: name of new fence
+ * @a: fence a
+ * @b: fence b
+ *
+ * Creates a new fence which contains copies of all the sync_pts in both
+ * @a and @b. @a and @b remain valid, independent fences.
+ */
+struct sync_fence *sync_fence_merge(const char *name,
+ struct sync_fence *a, struct sync_fence *b);
+
+/**
+ * sync_fence_fdget() - get a fence from an fd
+ * @fd: fd referencing a fence
+ *
+ * Ensures @fd references a valid fence, increments the refcount of the backing
+ * file, and returns the fence.
+ */
+struct sync_fence *sync_fence_fdget(int fd);
+
+/**
+ * sync_fence_put() - puts a refernnce of a sync fence
+ * @fence: fence to put
+ *
+ * Puts a reference on @fence. If this is the last reference, the fence and
+ * all it's sync_pts will be freed
+ */
+void sync_fence_put(struct sync_fence *fence);
+
+/**
+ * sync_fence_install() - installs a fence into a file descriptor
+ * @fence: fence to instal
+ * @fd: file descriptor in which to install the fence
+ *
+ * Installs @fence into @fd. @fd's should be acquired through get_unused_fd().
+ */
+void sync_fence_install(struct sync_fence *fence, int fd);
+
+/**
+ * sync_fence_wait_async() - registers and async wait on the fence
+ * @fence: fence to wait on
+ * @callback: callback
+ * @callback_data data to pass to the callback
+ *
+ * Returns 1 if @fence has already signaled.
+ *
+ * Registers a callback to be called when @fence signals or has an error
+ */
+int sync_fence_wait_async(struct sync_fence *fence,
+ void (*callback)(struct sync_fence *, void *data),
+ void *callback_data);
+
+/**
+ * sync_fence_wait() - wait on fence
+ * @fence: fence to wait on
+ * @tiemout: timeout in ms
+ *
+ * Wait for @fence to be signaled or have an error. Waits indefintly
+ * if @timeout = 0
+ */
+int sync_fence_wait(struct sync_fence *fence, long timeout);
+
+/* useful for sync driver's debug print handlers */
+const char *sync_status_str(int status);
+
+#endif /* __KERNEL__ */
+
+/**
+ * struct sync_merge_data - data passed to merge ioctl
+ * @fd2: file descriptor of second fence
+ * @name: name of new fence
+ * @fence: returns the fd of the new fence to userspace
+ */
+struct sync_merge_data {
+ __s32 fd2; /* fd of second fence */
+ char name[32]; /* name of new fence */
+ __s32 fence; /* fd on newly created fence */
+};
+
+#define SYNC_IOC_MAGIC '>'
+
+/**
+ * DOC: SYNC_IOC_WAIT - wait for a fence to signal
+ *
+ * pass timeout in milliseconds.
+ */
+#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __u32)
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data. Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
+ * new fence's fd in sync_merge_data.fence
+ */
+#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
+
+#endif /* _LINUX_SYNC_H */
In complicated DMA pipelines such as graphics (multimedia, camera, gpu, display) a consumer of a buffer needs to know when the producer has finished producing it. Likewise the producer needs to know when the consumer is finished with the buffer so it can reuse it. A particular buffer may be consumed by multiple consumers which will retain the buffer for different amounts of time. In addition, a consumer may consume multiple buffers atomically. The sync framework adds an API which allows synchronization between the producers and consumers in a generic way while also allowing platforms which have shared hardware synchronization primitives to exploit them. See Documentation/sync.txt for more information Signed-off-by: Erik Gilling <konkers@android.com> --- Documentation/sync.txt | 75 +++++++ drivers/base/Kconfig | 8 + drivers/base/Makefile | 2 + drivers/base/sync.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sync.h | 316 +++++++++++++++++++++++++++++ 5 files changed, 926 insertions(+), 0 deletions(-) create mode 100644 Documentation/sync.txt create mode 100644 drivers/base/sync.c create mode 100644 include/linux/sync.h