From d0b2f91bede3bd5e3d24dd6803e56eee959c1797 Mon Sep 17 00:00:00 2001 From: André Fabian Silva Delgado Date: Thu, 20 Oct 2016 00:10:27 -0300 Subject: Linux-libre 4.8.2-gnu --- drivers/staging/android/sw_sync.c | 341 +++++++++++++++++++++++++++++++++----- 1 file changed, 298 insertions(+), 43 deletions(-) (limited to 'drivers/staging/android/sw_sync.c') diff --git a/drivers/staging/android/sw_sync.c b/drivers/staging/android/sw_sync.c index af39ff58f..115c91747 100644 --- a/drivers/staging/android/sw_sync.c +++ b/drivers/staging/android/sw_sync.c @@ -1,5 +1,5 @@ /* - * drivers/base/sw_sync.c + * drivers/dma-buf/sw_sync.c * * Copyright (C) 2012 Google, Inc. * @@ -14,76 +14,331 @@ * */ -#include -#include -#include #include #include -#include -#include #include +#include +#include -#include "sw_sync.h" +#include "sync_debug.h" -struct fence *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) +#define CREATE_TRACE_POINTS +#include "trace/sync.h" + +struct sw_sync_create_fence_data { + __u32 value; + char name[32]; + __s32 fence; /* fd of new fence */ +}; + +#define SW_SYNC_IOC_MAGIC 'W' + +#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ + struct sw_sync_create_fence_data) +#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32) + +static const struct fence_ops timeline_fence_ops; + +static inline struct sync_pt *fence_to_sync_pt(struct fence *fence) +{ + if (fence->ops != &timeline_fence_ops) + return NULL; + return container_of(fence, struct sync_pt, base); +} + +/** + * sync_timeline_create() - creates a sync object + * @name: sync_timeline name + * + * Creates a new sync_timeline. Returns the sync_timeline object or NULL in + * case of error. + */ +struct sync_timeline *sync_timeline_create(const char *name) +{ + struct sync_timeline *obj; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return NULL; + + kref_init(&obj->kref); + obj->context = fence_context_alloc(1); + strlcpy(obj->name, name, sizeof(obj->name)); + + INIT_LIST_HEAD(&obj->child_list_head); + INIT_LIST_HEAD(&obj->active_list_head); + spin_lock_init(&obj->child_list_lock); + + sync_timeline_debug_add(obj); + + return obj; +} + +static void sync_timeline_free(struct kref *kref) +{ + struct sync_timeline *obj = + container_of(kref, struct sync_timeline, kref); + + sync_timeline_debug_remove(obj); + + kfree(obj); +} + +static void sync_timeline_get(struct sync_timeline *obj) +{ + kref_get(&obj->kref); +} + +static void sync_timeline_put(struct sync_timeline *obj) +{ + kref_put(&obj->kref, sync_timeline_free); +} + +/** + * sync_timeline_signal() - signal a status change on a sync_timeline + * @obj: sync_timeline to signal + * @inc: num to increment on timeline->value + * + * A sync implementation should call this any time one of it's fences + * has signaled or has an error condition. + */ +static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc) +{ + unsigned long flags; + struct sync_pt *pt, *next; + + trace_sync_timeline(obj); + + spin_lock_irqsave(&obj->child_list_lock, flags); + + obj->value += inc; + + list_for_each_entry_safe(pt, next, &obj->active_list_head, + active_list) { + if (fence_is_signaled_locked(&pt->base)) + list_del_init(&pt->active_list); + } + + spin_unlock_irqrestore(&obj->child_list_lock, flags); +} + +/** + * sync_pt_create() - creates a sync pt + * @parent: fence's parent sync_timeline + * @size: size to allocate for this pt + * @inc: value of the fence + * + * Creates a new sync_pt as a child of @parent. @size bytes will be + * allocated allowing for implementation specific data to be kept after + * the generic sync_timeline struct. Returns the sync_pt object or + * NULL in case of error. + */ +static struct sync_pt *sync_pt_create(struct sync_timeline *obj, int size, + unsigned int value) +{ + unsigned long flags; + struct sync_pt *pt; + + if (size < sizeof(*pt)) + return NULL; + + pt = kzalloc(size, GFP_KERNEL); + if (!pt) + return NULL; + + spin_lock_irqsave(&obj->child_list_lock, flags); + sync_timeline_get(obj); + fence_init(&pt->base, &timeline_fence_ops, &obj->child_list_lock, + obj->context, value); + list_add_tail(&pt->child_list, &obj->child_list_head); + INIT_LIST_HEAD(&pt->active_list); + spin_unlock_irqrestore(&obj->child_list_lock, flags); + return pt; +} + +static const char *timeline_fence_get_driver_name(struct fence *fence) +{ + return "sw_sync"; +} + +static const char *timeline_fence_get_timeline_name(struct fence *fence) +{ + struct sync_timeline *parent = fence_parent(fence); + + return parent->name; +} + +static void timeline_fence_release(struct fence *fence) { - struct sw_sync_pt *pt; + struct sync_pt *pt = fence_to_sync_pt(fence); + struct sync_timeline *parent = fence_parent(fence); + unsigned long flags; - pt = (struct sw_sync_pt *) - sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt)); + spin_lock_irqsave(fence->lock, flags); + list_del(&pt->child_list); + if (WARN_ON_ONCE(!list_empty(&pt->active_list))) + list_del(&pt->active_list); + spin_unlock_irqrestore(fence->lock, flags); - pt->value = value; + sync_timeline_put(parent); + fence_free(fence); +} + +static bool timeline_fence_signaled(struct fence *fence) +{ + struct sync_timeline *parent = fence_parent(fence); - return (struct fence *)pt; + return (fence->seqno > parent->value) ? false : true; } -EXPORT_SYMBOL(sw_sync_pt_create); -static int sw_sync_fence_has_signaled(struct fence *fence) +static bool timeline_fence_enable_signaling(struct fence *fence) { - struct sw_sync_pt *pt = (struct sw_sync_pt *)fence; - struct sw_sync_timeline *obj = - (struct sw_sync_timeline *)fence_parent(fence); + struct sync_pt *pt = fence_to_sync_pt(fence); + struct sync_timeline *parent = fence_parent(fence); - return (pt->value > obj->value) ? 0 : 1; + if (timeline_fence_signaled(fence)) + return false; + + list_add_tail(&pt->active_list, &parent->active_list_head); + return true; } -static void sw_sync_timeline_value_str(struct sync_timeline *sync_timeline, - char *str, int size) +static void timeline_fence_value_str(struct fence *fence, + char *str, int size) { - struct sw_sync_timeline *timeline = - (struct sw_sync_timeline *)sync_timeline; - snprintf(str, size, "%d", timeline->value); + snprintf(str, size, "%d", fence->seqno); } -static void sw_sync_fence_value_str(struct fence *fence, char *str, int size) +static void timeline_fence_timeline_value_str(struct fence *fence, + char *str, int size) { - struct sw_sync_pt *pt = (struct sw_sync_pt *)fence; + struct sync_timeline *parent = fence_parent(fence); - snprintf(str, size, "%d", pt->value); + snprintf(str, size, "%d", parent->value); } -static struct sync_timeline_ops sw_sync_timeline_ops = { - .driver_name = "sw_sync", - .has_signaled = sw_sync_fence_has_signaled, - .timeline_value_str = sw_sync_timeline_value_str, - .fence_value_str = sw_sync_fence_value_str, +static const struct fence_ops timeline_fence_ops = { + .get_driver_name = timeline_fence_get_driver_name, + .get_timeline_name = timeline_fence_get_timeline_name, + .enable_signaling = timeline_fence_enable_signaling, + .signaled = timeline_fence_signaled, + .wait = fence_default_wait, + .release = timeline_fence_release, + .fence_value_str = timeline_fence_value_str, + .timeline_value_str = timeline_fence_timeline_value_str, }; -struct sw_sync_timeline *sw_sync_timeline_create(const char *name) +/* + * *WARNING* + * + * improper use of this can result in deadlocking kernel drivers from userspace. + */ + +/* opening sw_sync create a new sync obj */ +static int sw_sync_debugfs_open(struct inode *inode, struct file *file) { - struct sw_sync_timeline *obj = (struct sw_sync_timeline *) - sync_timeline_create(&sw_sync_timeline_ops, - sizeof(struct sw_sync_timeline), - name); + struct sync_timeline *obj; + char task_comm[TASK_COMM_LEN]; - return obj; + get_task_comm(task_comm, current); + + obj = sync_timeline_create(task_comm); + if (!obj) + return -ENOMEM; + + file->private_data = obj; + + return 0; } -EXPORT_SYMBOL(sw_sync_timeline_create); -void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) +static int sw_sync_debugfs_release(struct inode *inode, struct file *file) { - obj->value += inc; + struct sync_timeline *obj = file->private_data; + + smp_wmb(); + + sync_timeline_put(obj); + return 0; +} + +static long sw_sync_ioctl_create_fence(struct sync_timeline *obj, + unsigned long arg) +{ + int fd = get_unused_fd_flags(O_CLOEXEC); + int err; + struct sync_pt *pt; + struct sync_file *sync_file; + struct sw_sync_create_fence_data data; + + if (fd < 0) + return fd; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { + err = -EFAULT; + goto err; + } + + pt = sync_pt_create(obj, sizeof(*pt), data.value); + if (!pt) { + err = -ENOMEM; + goto err; + } + + sync_file = sync_file_create(&pt->base); + if (!sync_file) { + fence_put(&pt->base); + err = -ENOMEM; + goto err; + } + + data.fence = fd; + if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + fput(sync_file->file); + err = -EFAULT; + goto err; + } + + fd_install(fd, sync_file->file); + + return 0; + +err: + put_unused_fd(fd); + return err; +} - sync_timeline_signal(&obj->obj); +static long sw_sync_ioctl_inc(struct sync_timeline *obj, unsigned long arg) +{ + u32 value; + + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) + return -EFAULT; + + sync_timeline_signal(obj, value); + + return 0; +} + +static long sw_sync_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct sync_timeline *obj = file->private_data; + + switch (cmd) { + case SW_SYNC_IOC_CREATE_FENCE: + return sw_sync_ioctl_create_fence(obj, arg); + + case SW_SYNC_IOC_INC: + return sw_sync_ioctl_inc(obj, arg); + + default: + return -ENOTTY; + } } -EXPORT_SYMBOL(sw_sync_timeline_inc); + +const struct file_operations sw_sync_debugfs_fops = { + .open = sw_sync_debugfs_open, + .release = sw_sync_debugfs_release, + .unlocked_ioctl = sw_sync_ioctl, + .compat_ioctl = sw_sync_ioctl, +}; -- cgit v1.2.3-54-g00ecf