From patchwork Wed Apr 27 03:32:12 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Stultz X-Patchwork-Id: 66767 Delivered-To: patches@linaro.org Received: by 10.140.93.198 with SMTP id d64csp1958300qge; Tue, 26 Apr 2016 20:32:15 -0700 (PDT) X-Received: by 10.98.55.129 with SMTP id e123mr8338415pfa.4.1461727935856; Tue, 26 Apr 2016 20:32:15 -0700 (PDT) Return-Path: Received: from mail-pf0-x236.google.com (mail-pf0-x236.google.com. [2607:f8b0:400e:c00::236]) by mx.google.com with ESMTPS id xo3si2607434pac.215.2016.04.26.20.32.15 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 26 Apr 2016 20:32:15 -0700 (PDT) Received-SPF: pass (google.com: domain of john.stultz@linaro.org designates 2607:f8b0:400e:c00::236 as permitted sender) client-ip=2607:f8b0:400e:c00::236; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: domain of john.stultz@linaro.org designates 2607:f8b0:400e:c00::236 as permitted sender) smtp.mailfrom=john.stultz@linaro.org; dmarc=pass (p=NONE dis=NONE) header.from=linaro.org Received: by mail-pf0-x236.google.com with SMTP id c189so14850380pfb.3 for ; Tue, 26 Apr 2016 20:32:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=TOLDDv0DEQrphLiS5mNdPDc7ZUTZye2NobmA1BLUA3g=; b=WS6CgK+0a1S601dW4qeFkQ2M1KpaF69MPCU3JfAZw9MeoYAwhgkxn/QwjjEU1nvZ+f hbJwkhheZMj2t1FElU3w77xq2ulF40aCGNGe4hFYjk9je06eBFgwid6uQ1S/JKezT9Jj BigtKBWwXnTYB76XeZPNdY6qvkeXNmpIXl/OU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=TOLDDv0DEQrphLiS5mNdPDc7ZUTZye2NobmA1BLUA3g=; b=bZQVNNaTwCg5WMD7bAqZ9y7NrbFrFbWHWcfwCG7D7qNb2YjBFajiSAa9/SCRjI5rZ/ 95lLN2lDWwcl7cIH0jdK1drLKODEJC9lavjYwVTFxbilidMt2WS82tUJZQiHO2PmSmMK pIgbck0MRJMjPWUmOedz2bIYc2F2qjxk1Hk7/VeYl8Qs2hv6LoMQeX75F0VsRc2nxoAF kXZHNaa5E2JNVZff64+KNopGdb6TcCeHFQu2JwxaL6VzaBzDeUsQpllsS/Jx2kKYfahl CXwQ9Dzxh/+g36qQynhvrAVPoJ4W4SCMW7YmZWmik2/GuIphfY2n/0NVTwkz35GWPGXD MT1w== X-Gm-Message-State: AOPr4FUhV67PD9BRHtrYICxh+7g/9xP7Z8U5JWiGCUSSoo87eYjNGCG9Sme9FfD7hXsN6EVmQ9Y= X-Received: by 10.98.71.80 with SMTP id u77mr8570217pfa.119.1461727935357; Tue, 26 Apr 2016 20:32:15 -0700 (PDT) Return-Path: Received: from localhost.localdomain (c-73-67-244-238.hsd1.or.comcast.net. [73.67.244.238]) by smtp.gmail.com with ESMTPSA id bk8sm8866527pac.3.2016.04.26.20.32.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 26 Apr 2016 20:32:14 -0700 (PDT) From: John Stultz To: Guodong Xu Cc: John Stultz Subject: [PATCH] HACK: Revert 4.6 sync changes to let graphics still work Date: Tue, 26 Apr 2016 20:32:12 -0700 Message-Id: <1461727932-5137-1-git-send-email-john.stultz@linaro.org> X-Mailer: git-send-email 1.9.1 Currently the sync abi changes in staging breaks graphics. So until we can modify userspace & mali drivers to use the new abi, revert the sync changes. This can probably be thinned down a bit, as its a whole-hog revert to 4.5 era code. Signed-off-by: John Stultz --- drivers/staging/android/Kconfig | 9 + drivers/staging/android/sw_sync.c | 191 +++++++++++++- drivers/staging/android/sw_sync.h | 8 +- drivers/staging/android/sync.c | 469 +++++++++++++++++++++++------------ drivers/staging/android/sync.h | 241 +++++++++++++----- drivers/staging/android/sync_debug.c | 221 +++++------------ drivers/staging/android/trace/sync.h | 44 ++++ drivers/staging/android/uapi/sync.h | 37 ++- 8 files changed, 815 insertions(+), 405 deletions(-) -- 1.9.1 diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index bd90d20..42b1512 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -57,6 +57,15 @@ config SW_SYNC synchronization. Useful when there is no hardware primitive backing the synchronization. +config SW_SYNC_USER + bool "Userspace API for SW_SYNC" + default n + depends on SW_SYNC + ---help--- + Provides a user space API to the sw sync object. + *WARNING* improper use of this can result in deadlocking kernel + drivers from userspace. + source "drivers/staging/android/ion/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/sw_sync.c b/drivers/staging/android/sw_sync.c index af39ff5..c4ff167 100644 --- a/drivers/staging/android/sw_sync.c +++ b/drivers/staging/android/sw_sync.c @@ -25,7 +25,15 @@ #include "sw_sync.h" -struct fence *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) +static int sw_sync_cmp(u32 a, u32 b) +{ + if (a == b) + return 0; + + return ((s32)a - (s32)b) < 0 ? -1 : 1; +} + +struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) { struct sw_sync_pt *pt; @@ -34,17 +42,47 @@ struct fence *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) pt->value = value; - return (struct fence *)pt; + return (struct sync_pt *)pt; } EXPORT_SYMBOL(sw_sync_pt_create); -static int sw_sync_fence_has_signaled(struct fence *fence) +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(sync_pt); + + return (struct sync_pt *)sw_sync_pt_create(obj, pt->value); +} + +static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt) { - struct sw_sync_pt *pt = (struct sw_sync_pt *)fence; + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; struct sw_sync_timeline *obj = - (struct sw_sync_timeline *)fence_parent(fence); + (struct sw_sync_timeline *)sync_pt_parent(sync_pt); + + return sw_sync_cmp(obj->value, pt->value) >= 0; +} + +static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b) +{ + struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a; + struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b; + + return sw_sync_cmp(pt_a->value, pt_b->value); +} + +static int sw_sync_fill_driver_data(struct sync_pt *sync_pt, + void *data, int size) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + + if (size < sizeof(pt->value)) + return -ENOMEM; - return (pt->value > obj->value) ? 0 : 1; + memcpy(data, &pt->value, sizeof(pt->value)); + + return sizeof(pt->value); } static void sw_sync_timeline_value_str(struct sync_timeline *sync_timeline, @@ -55,18 +93,22 @@ static void sw_sync_timeline_value_str(struct sync_timeline *sync_timeline, snprintf(str, size, "%d", timeline->value); } -static void sw_sync_fence_value_str(struct fence *fence, char *str, int size) +static void sw_sync_pt_value_str(struct sync_pt *sync_pt, + char *str, int size) { - struct sw_sync_pt *pt = (struct sw_sync_pt *)fence; + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; snprintf(str, size, "%d", pt->value); } static struct sync_timeline_ops sw_sync_timeline_ops = { .driver_name = "sw_sync", - .has_signaled = sw_sync_fence_has_signaled, + .dup = sw_sync_pt_dup, + .has_signaled = sw_sync_pt_has_signaled, + .compare = sw_sync_pt_compare, + .fill_driver_data = sw_sync_fill_driver_data, .timeline_value_str = sw_sync_timeline_value_str, - .fence_value_str = sw_sync_fence_value_str, + .pt_value_str = sw_sync_pt_value_str, }; struct sw_sync_timeline *sw_sync_timeline_create(const char *name) @@ -87,3 +129,132 @@ void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) sync_timeline_signal(&obj->obj); } EXPORT_SYMBOL(sw_sync_timeline_inc); + +#ifdef CONFIG_SW_SYNC_USER +/* *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_open(struct inode *inode, struct file *file) +{ + struct sw_sync_timeline *obj; + char task_comm[TASK_COMM_LEN]; + + get_task_comm(task_comm, current); + + obj = sw_sync_timeline_create(task_comm); + if (!obj) + return -ENOMEM; + + file->private_data = obj; + + return 0; +} + +static int sw_sync_release(struct inode *inode, struct file *file) +{ + struct sw_sync_timeline *obj = file->private_data; + + sync_timeline_destroy(&obj->obj); + return 0; +} + +static long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, + unsigned long arg) +{ + int fd = get_unused_fd_flags(O_CLOEXEC); + int err; + struct sync_pt *pt; + struct sync_fence *fence; + 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 = sw_sync_pt_create(obj, data.value); + if (!pt) { + err = -ENOMEM; + goto err; + } + + data.name[sizeof(data.name) - 1] = '\0'; + fence = sync_fence_create(data.name, pt); + if (!fence) { + sync_pt_free(pt); + err = -ENOMEM; + goto err; + } + + data.fence = fd; + if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + sync_fence_put(fence); + err = -EFAULT; + goto err; + } + + sync_fence_install(fence, fd); + + return 0; + +err: + put_unused_fd(fd); + return err; +} + +static long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg) +{ + u32 value; + + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) + return -EFAULT; + + sw_sync_timeline_inc(obj, value); + + return 0; +} + +static long sw_sync_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct sw_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; + } +} + +static const struct file_operations sw_sync_fops = { + .owner = THIS_MODULE, + .open = sw_sync_open, + .release = sw_sync_release, + .unlocked_ioctl = sw_sync_ioctl, + .compat_ioctl = sw_sync_ioctl, +}; + +static struct miscdevice sw_sync_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sw_sync", + .fops = &sw_sync_fops, +}; + +static int __init sw_sync_device_init(void) +{ + return misc_register(&sw_sync_dev); +} +device_initcall(sw_sync_device_init); + +#endif /* CONFIG_SW_SYNC_USER */ diff --git a/drivers/staging/android/sw_sync.h b/drivers/staging/android/sw_sync.h index e18667b..c87ae9e 100644 --- a/drivers/staging/android/sw_sync.h +++ b/drivers/staging/android/sw_sync.h @@ -29,7 +29,7 @@ struct sw_sync_timeline { }; struct sw_sync_pt { - struct fence pt; + struct sync_pt pt; u32 value; }; @@ -38,7 +38,7 @@ struct sw_sync_pt { struct sw_sync_timeline *sw_sync_timeline_create(const char *name); void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc); -struct fence *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value); +struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value); #else static inline struct sw_sync_timeline *sw_sync_timeline_create(const char *name) { @@ -49,8 +49,8 @@ static inline void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) { } -static inline struct fence *sw_sync_pt_create(struct sw_sync_timeline *obj, - u32 value) +static inline struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, + u32 value) { return NULL; } diff --git a/drivers/staging/android/sync.c b/drivers/staging/android/sync.c index 3a8f210..ed43796 100644 --- a/drivers/staging/android/sync.c +++ b/drivers/staging/android/sync.c @@ -32,7 +32,7 @@ #include "trace/sync.h" static const struct fence_ops android_fence_ops; -static const struct file_operations sync_file_fops; +static const struct file_operations sync_fence_fops; struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, int size, const char *name) @@ -68,6 +68,9 @@ static void sync_timeline_free(struct kref *kref) sync_timeline_debug_remove(obj); + if (obj->ops->release_obj) + obj->ops->release_obj(obj); + kfree(obj); } @@ -90,6 +93,10 @@ void sync_timeline_destroy(struct sync_timeline *obj) */ smp_wmb(); + /* + * signal any children that their parent is going away. + */ + sync_timeline_signal(obj); sync_timeline_put(obj); } EXPORT_SYMBOL(sync_timeline_destroy); @@ -97,115 +104,126 @@ EXPORT_SYMBOL(sync_timeline_destroy); void sync_timeline_signal(struct sync_timeline *obj) { unsigned long flags; - struct fence *fence, *next; + LIST_HEAD(signaled_pts); + struct sync_pt *pt, *next; trace_sync_timeline(obj); spin_lock_irqsave(&obj->child_list_lock, flags); - list_for_each_entry_safe(fence, next, &obj->active_list_head, + list_for_each_entry_safe(pt, next, &obj->active_list_head, active_list) { - if (fence_is_signaled_locked(fence)) - list_del_init(&fence->active_list); + if (fence_is_signaled_locked(&pt->base)) + list_del_init(&pt->active_list); } spin_unlock_irqrestore(&obj->child_list_lock, flags); } EXPORT_SYMBOL(sync_timeline_signal); -struct fence *sync_pt_create(struct sync_timeline *obj, int size) +struct sync_pt *sync_pt_create(struct sync_timeline *obj, int size) { unsigned long flags; - struct fence *fence; + struct sync_pt *pt; - if (size < sizeof(*fence)) + if (size < sizeof(struct sync_pt)) return NULL; - fence = kzalloc(size, GFP_KERNEL); - if (!fence) + pt = kzalloc(size, GFP_KERNEL); + if (!pt) return NULL; spin_lock_irqsave(&obj->child_list_lock, flags); sync_timeline_get(obj); - fence_init(fence, &android_fence_ops, &obj->child_list_lock, + fence_init(&pt->base, &android_fence_ops, &obj->child_list_lock, obj->context, ++obj->value); - list_add_tail(&fence->child_list, &obj->child_list_head); - INIT_LIST_HEAD(&fence->active_list); + 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 fence; + return pt; } EXPORT_SYMBOL(sync_pt_create); -static struct sync_file *sync_file_alloc(int size, const char *name) +void sync_pt_free(struct sync_pt *pt) +{ + fence_put(&pt->base); +} +EXPORT_SYMBOL(sync_pt_free); + +static struct sync_fence *sync_fence_alloc(int size, const char *name) { - struct sync_file *sync_file; + struct sync_fence *fence; - sync_file = kzalloc(size, GFP_KERNEL); - if (!sync_file) + fence = kzalloc(size, GFP_KERNEL); + if (!fence) return NULL; - sync_file->file = anon_inode_getfile("sync_file", &sync_file_fops, - sync_file, 0); - if (IS_ERR(sync_file->file)) + fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops, + fence, 0); + if (IS_ERR(fence->file)) goto err; - kref_init(&sync_file->kref); - strlcpy(sync_file->name, name, sizeof(sync_file->name)); + kref_init(&fence->kref); + strlcpy(fence->name, name, sizeof(fence->name)); - init_waitqueue_head(&sync_file->wq); + init_waitqueue_head(&fence->wq); - return sync_file; + return fence; err: - kfree(sync_file); + kfree(fence); return NULL; } static void fence_check_cb_func(struct fence *f, struct fence_cb *cb) { - struct sync_file_cb *check; - struct sync_file *sync_file; + struct sync_fence_cb *check; + struct sync_fence *fence; - check = container_of(cb, struct sync_file_cb, cb); - sync_file = check->sync_file; + check = container_of(cb, struct sync_fence_cb, cb); + fence = check->fence; - if (atomic_dec_and_test(&sync_file->status)) - wake_up_all(&sync_file->wq); + if (atomic_dec_and_test(&fence->status)) + wake_up_all(&fence->wq); } -/* TODO: implement a create which takes more that one fence */ -struct sync_file *sync_file_create(const char *name, struct fence *fence) +/* TODO: implement a create which takes more that one sync_pt */ +struct sync_fence *sync_fence_create_dma(const char *name, struct fence *pt) { - struct sync_file *sync_file; + struct sync_fence *fence; - sync_file = sync_file_alloc(offsetof(struct sync_file, cbs[1]), - name); - if (!sync_file) + fence = sync_fence_alloc(offsetof(struct sync_fence, cbs[1]), name); + if (!fence) return NULL; - sync_file->num_fences = 1; - atomic_set(&sync_file->status, 1); + fence->num_fences = 1; + atomic_set(&fence->status, 1); - sync_file->cbs[0].fence = fence; - sync_file->cbs[0].sync_file = sync_file; - if (fence_add_callback(fence, &sync_file->cbs[0].cb, - fence_check_cb_func)) - atomic_dec(&sync_file->status); + fence->cbs[0].sync_pt = pt; + fence->cbs[0].fence = fence; + if (fence_add_callback(pt, &fence->cbs[0].cb, fence_check_cb_func)) + atomic_dec(&fence->status); - sync_file_debug_add(sync_file); + sync_fence_debug_add(fence); - return sync_file; + return fence; } -EXPORT_SYMBOL(sync_file_create); +EXPORT_SYMBOL(sync_fence_create_dma); -struct sync_file *sync_file_fdget(int fd) +struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt) +{ + return sync_fence_create_dma(name, &pt->base); +} +EXPORT_SYMBOL(sync_fence_create); + +struct sync_fence *sync_fence_fdget(int fd) { struct file *file = fget(fd); if (!file) return NULL; - if (file->f_op != &sync_file_fops) + if (file->f_op != &sync_fence_fops) goto err; return file->private_data; @@ -214,71 +232,70 @@ err: fput(file); return NULL; } -EXPORT_SYMBOL(sync_file_fdget); +EXPORT_SYMBOL(sync_fence_fdget); -void sync_file_put(struct sync_file *sync_file) +void sync_fence_put(struct sync_fence *fence) { - fput(sync_file->file); + fput(fence->file); } -EXPORT_SYMBOL(sync_file_put); +EXPORT_SYMBOL(sync_fence_put); -void sync_file_install(struct sync_file *sync_file, int fd) +void sync_fence_install(struct sync_fence *fence, int fd) { - fd_install(fd, sync_file->file); + fd_install(fd, fence->file); } -EXPORT_SYMBOL(sync_file_install); +EXPORT_SYMBOL(sync_fence_install); -static void sync_file_add_pt(struct sync_file *sync_file, int *i, - struct fence *fence) +static void sync_fence_add_pt(struct sync_fence *fence, + int *i, struct fence *pt) { - sync_file->cbs[*i].fence = fence; - sync_file->cbs[*i].sync_file = sync_file; + fence->cbs[*i].sync_pt = pt; + fence->cbs[*i].fence = fence; - if (!fence_add_callback(fence, &sync_file->cbs[*i].cb, - fence_check_cb_func)) { - fence_get(fence); + if (!fence_add_callback(pt, &fence->cbs[*i].cb, fence_check_cb_func)) { + fence_get(pt); (*i)++; } } -struct sync_file *sync_file_merge(const char *name, - struct sync_file *a, struct sync_file *b) +struct sync_fence *sync_fence_merge(const char *name, + struct sync_fence *a, struct sync_fence *b) { int num_fences = a->num_fences + b->num_fences; - struct sync_file *sync_file; + struct sync_fence *fence; int i, i_a, i_b; - unsigned long size = offsetof(struct sync_file, cbs[num_fences]); + unsigned long size = offsetof(struct sync_fence, cbs[num_fences]); - sync_file = sync_file_alloc(size, name); - if (!sync_file) + fence = sync_fence_alloc(size, name); + if (!fence) return NULL; - atomic_set(&sync_file->status, num_fences); + atomic_set(&fence->status, num_fences); /* - * Assume sync_file a and b are both ordered and have no + * Assume sync_fence a and b are both ordered and have no * duplicates with the same context. * - * If a sync_file can only be created with sync_file_merge - * and sync_file_create, this is a reasonable assumption. + * If a sync_fence can only be created with sync_fence_merge + * and sync_fence_create, this is a reasonable assumption. */ for (i = i_a = i_b = 0; i_a < a->num_fences && i_b < b->num_fences; ) { - struct fence *pt_a = a->cbs[i_a].fence; - struct fence *pt_b = b->cbs[i_b].fence; + struct fence *pt_a = a->cbs[i_a].sync_pt; + struct fence *pt_b = b->cbs[i_b].sync_pt; if (pt_a->context < pt_b->context) { - sync_file_add_pt(sync_file, &i, pt_a); + sync_fence_add_pt(fence, &i, pt_a); i_a++; } else if (pt_a->context > pt_b->context) { - sync_file_add_pt(sync_file, &i, pt_b); + sync_fence_add_pt(fence, &i, pt_b); i_b++; } else { if (pt_a->seqno - pt_b->seqno <= INT_MAX) - sync_file_add_pt(sync_file, &i, pt_a); + sync_fence_add_pt(fence, &i, pt_a); else - sync_file_add_pt(sync_file, &i, pt_b); + sync_fence_add_pt(fence, &i, pt_b); i_a++; i_b++; @@ -286,55 +303,156 @@ struct sync_file *sync_file_merge(const char *name, } for (; i_a < a->num_fences; i_a++) - sync_file_add_pt(sync_file, &i, a->cbs[i_a].fence); + sync_fence_add_pt(fence, &i, a->cbs[i_a].sync_pt); for (; i_b < b->num_fences; i_b++) - sync_file_add_pt(sync_file, &i, b->cbs[i_b].fence); + sync_fence_add_pt(fence, &i, b->cbs[i_b].sync_pt); if (num_fences > i) - atomic_sub(num_fences - i, &sync_file->status); - sync_file->num_fences = i; + atomic_sub(num_fences - i, &fence->status); + fence->num_fences = i; + + sync_fence_debug_add(fence); + return fence; +} +EXPORT_SYMBOL(sync_fence_merge); + +int sync_fence_wake_up_wq(wait_queue_t *curr, unsigned mode, + int wake_flags, void *key) +{ + struct sync_fence_waiter *wait; + + wait = container_of(curr, struct sync_fence_waiter, work); + list_del_init(&wait->work.task_list); + + wait->callback(wait->work.private, wait); + return 1; +} + +int sync_fence_wait_async(struct sync_fence *fence, + struct sync_fence_waiter *waiter) +{ + int err = atomic_read(&fence->status); + unsigned long flags; + + if (err < 0) + return err; + + if (!err) + return 1; + + init_waitqueue_func_entry(&waiter->work, sync_fence_wake_up_wq); + waiter->work.private = fence; + + spin_lock_irqsave(&fence->wq.lock, flags); + err = atomic_read(&fence->status); + if (err > 0) + __add_wait_queue_tail(&fence->wq, &waiter->work); + spin_unlock_irqrestore(&fence->wq.lock, flags); + + if (err < 0) + return err; + + return !err; +} +EXPORT_SYMBOL(sync_fence_wait_async); + +int sync_fence_cancel_async(struct sync_fence *fence, + struct sync_fence_waiter *waiter) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&fence->wq.lock, flags); + if (!list_empty(&waiter->work.task_list)) + list_del_init(&waiter->work.task_list); + else + ret = -ENOENT; + spin_unlock_irqrestore(&fence->wq.lock, flags); + return ret; +} +EXPORT_SYMBOL(sync_fence_cancel_async); + +int sync_fence_wait(struct sync_fence *fence, long timeout) +{ + long ret; + int i; - sync_file_debug_add(sync_file); - return sync_file; + if (timeout < 0) + timeout = MAX_SCHEDULE_TIMEOUT; + else + timeout = msecs_to_jiffies(timeout); + + trace_sync_wait(fence, 1); + for (i = 0; i < fence->num_fences; ++i) + trace_sync_pt(fence->cbs[i].sync_pt); + ret = wait_event_interruptible_timeout(fence->wq, + atomic_read(&fence->status) <= 0, + timeout); + trace_sync_wait(fence, 0); + + if (ret < 0) { + return ret; + } else if (ret == 0) { + if (timeout) { + pr_info("fence timeout on [%p] after %dms\n", fence, + jiffies_to_msecs(timeout)); + sync_dump(); + } + return -ETIME; + } + + ret = atomic_read(&fence->status); + if (ret) { + pr_info("fence error %ld on [%p]\n", ret, fence); + sync_dump(); + } + return ret; } -EXPORT_SYMBOL(sync_file_merge); +EXPORT_SYMBOL(sync_fence_wait); static const char *android_fence_get_driver_name(struct fence *fence) { - struct sync_timeline *parent = fence_parent(fence); + struct sync_pt *pt = container_of(fence, struct sync_pt, base); + struct sync_timeline *parent = sync_pt_parent(pt); return parent->ops->driver_name; } static const char *android_fence_get_timeline_name(struct fence *fence) { - struct sync_timeline *parent = fence_parent(fence); + struct sync_pt *pt = container_of(fence, struct sync_pt, base); + struct sync_timeline *parent = sync_pt_parent(pt); return parent->name; } static void android_fence_release(struct fence *fence) { - struct sync_timeline *parent = fence_parent(fence); + struct sync_pt *pt = container_of(fence, struct sync_pt, base); + struct sync_timeline *parent = sync_pt_parent(pt); unsigned long flags; spin_lock_irqsave(fence->lock, flags); - list_del(&fence->child_list); - if (WARN_ON_ONCE(!list_empty(&fence->active_list))) - list_del(&fence->active_list); + 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); + if (parent->ops->free_pt) + parent->ops->free_pt(pt); + sync_timeline_put(parent); - fence_free(fence); + fence_free(&pt->base); } static bool android_fence_signaled(struct fence *fence) { - struct sync_timeline *parent = fence_parent(fence); + struct sync_pt *pt = container_of(fence, struct sync_pt, base); + struct sync_timeline *parent = sync_pt_parent(pt); int ret; - ret = parent->ops->has_signaled(fence); + ret = parent->ops->has_signaled(pt); if (ret < 0) fence->status = ret; return ret; @@ -342,32 +460,46 @@ static bool android_fence_signaled(struct fence *fence) static bool android_fence_enable_signaling(struct fence *fence) { - struct sync_timeline *parent = fence_parent(fence); + struct sync_pt *pt = container_of(fence, struct sync_pt, base); + struct sync_timeline *parent = sync_pt_parent(pt); if (android_fence_signaled(fence)) return false; - list_add_tail(&fence->active_list, &parent->active_list_head); + list_add_tail(&pt->active_list, &parent->active_list_head); return true; } +static int android_fence_fill_driver_data(struct fence *fence, + void *data, int size) +{ + struct sync_pt *pt = container_of(fence, struct sync_pt, base); + struct sync_timeline *parent = sync_pt_parent(pt); + + if (!parent->ops->fill_driver_data) + return 0; + return parent->ops->fill_driver_data(pt, data, size); +} + static void android_fence_value_str(struct fence *fence, char *str, int size) { - struct sync_timeline *parent = fence_parent(fence); + struct sync_pt *pt = container_of(fence, struct sync_pt, base); + struct sync_timeline *parent = sync_pt_parent(pt); - if (!parent->ops->fence_value_str) { + if (!parent->ops->pt_value_str) { if (size) *str = 0; return; } - parent->ops->fence_value_str(fence, str, size); + parent->ops->pt_value_str(pt, str, size); } static void android_fence_timeline_value_str(struct fence *fence, char *str, int size) { - struct sync_timeline *parent = fence_parent(fence); + struct sync_pt *pt = container_of(fence, struct sync_pt, base); + struct sync_timeline *parent = sync_pt_parent(pt); if (!parent->ops->timeline_value_str) { if (size) @@ -384,57 +516,65 @@ static const struct fence_ops android_fence_ops = { .signaled = android_fence_signaled, .wait = fence_default_wait, .release = android_fence_release, + .fill_driver_data = android_fence_fill_driver_data, .fence_value_str = android_fence_value_str, .timeline_value_str = android_fence_timeline_value_str, }; -static void sync_file_free(struct kref *kref) +static void sync_fence_free(struct kref *kref) { - struct sync_file *sync_file = container_of(kref, struct sync_file, - kref); + struct sync_fence *fence = container_of(kref, struct sync_fence, kref); int i; - for (i = 0; i < sync_file->num_fences; ++i) { - fence_remove_callback(sync_file->cbs[i].fence, - &sync_file->cbs[i].cb); - fence_put(sync_file->cbs[i].fence); + for (i = 0; i < fence->num_fences; ++i) { + fence_remove_callback(fence->cbs[i].sync_pt, &fence->cbs[i].cb); + fence_put(fence->cbs[i].sync_pt); } - kfree(sync_file); + kfree(fence); } -static int sync_file_release(struct inode *inode, struct file *file) +static int sync_fence_release(struct inode *inode, struct file *file) { - struct sync_file *sync_file = file->private_data; + struct sync_fence *fence = file->private_data; - sync_file_debug_remove(sync_file); + sync_fence_debug_remove(fence); - kref_put(&sync_file->kref, sync_file_free); + kref_put(&fence->kref, sync_fence_free); return 0; } -static unsigned int sync_file_poll(struct file *file, poll_table *wait) +static unsigned int sync_fence_poll(struct file *file, poll_table *wait) { - struct sync_file *sync_file = file->private_data; + struct sync_fence *fence = file->private_data; int status; - poll_wait(file, &sync_file->wq, wait); + poll_wait(file, &fence->wq, wait); - status = atomic_read(&sync_file->status); + status = atomic_read(&fence->status); if (!status) return POLLIN; - if (status < 0) + else if (status < 0) return POLLERR; return 0; } -static long sync_file_ioctl_merge(struct sync_file *sync_file, - unsigned long arg) +static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg) +{ + __s32 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_flags(O_CLOEXEC); int err; - struct sync_file *fence2, *fence3; + struct sync_fence *fence2, *fence3; struct sync_merge_data data; if (fd < 0) @@ -445,14 +585,14 @@ static long sync_file_ioctl_merge(struct sync_file *sync_file, goto err_put_fd; } - fence2 = sync_file_fdget(data.fd2); + fence2 = sync_fence_fdget(data.fd2); if (!fence2) { err = -ENOENT; goto err_put_fd; } data.name[sizeof(data.name) - 1] = '\0'; - fence3 = sync_file_merge(data.name, sync_file, fence2); + fence3 = sync_fence_merge(data.name, fence, fence2); if (!fence3) { err = -ENOMEM; goto err_put_fence2; @@ -464,28 +604,40 @@ static long sync_file_ioctl_merge(struct sync_file *sync_file, goto err_put_fence3; } - sync_file_install(fence3, fd); - sync_file_put(fence2); + sync_fence_install(fence3, fd); + sync_fence_put(fence2); return 0; err_put_fence3: - sync_file_put(fence3); + sync_fence_put(fence3); err_put_fence2: - sync_file_put(fence2); + sync_fence_put(fence2); err_put_fd: put_unused_fd(fd); return err; } -static int sync_fill_fence_info(struct fence *fence, void *data, int size) +static int sync_fill_pt_info(struct fence *fence, void *data, int size) { - struct sync_fence_info *info = data; + struct sync_pt_info *info = data; + int ret; - if (size < sizeof(*info)) + if (size < sizeof(struct sync_pt_info)) return -ENOMEM; + info->len = sizeof(struct sync_pt_info); + + if (fence->ops->fill_driver_data) { + ret = fence->ops->fill_driver_data(fence, info->driver_data, + size - sizeof(*info)); + if (ret < 0) + return ret; + + info->len += ret; + } + strlcpy(info->obj_name, fence->ops->get_timeline_name(fence), sizeof(info->obj_name)); strlcpy(info->driver_name, fence->ops->get_driver_name(fence), @@ -496,13 +648,13 @@ static int sync_fill_fence_info(struct fence *fence, void *data, int size) info->status = 0; info->timestamp_ns = ktime_to_ns(fence->timestamp); - return sizeof(*info); + return info->len; } -static long sync_file_ioctl_fence_info(struct sync_file *sync_file, +static long sync_fence_ioctl_fence_info(struct sync_fence *fence, unsigned long arg) { - struct sync_file_info *info; + struct sync_fence_info_data *data; __u32 size; __u32 len = 0; int ret, i; @@ -510,27 +662,27 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file, if (copy_from_user(&size, (void __user *)arg, sizeof(size))) return -EFAULT; - if (size < sizeof(struct sync_file_info)) + if (size < sizeof(struct sync_fence_info_data)) return -EINVAL; if (size > 4096) size = 4096; - info = kzalloc(size, GFP_KERNEL); - if (!info) + data = kzalloc(size, GFP_KERNEL); + if (!data) return -ENOMEM; - strlcpy(info->name, sync_file->name, sizeof(info->name)); - info->status = atomic_read(&sync_file->status); - if (info->status >= 0) - info->status = !info->status; + strlcpy(data->name, fence->name, sizeof(data->name)); + data->status = atomic_read(&fence->status); + if (data->status >= 0) + data->status = !data->status; - len = sizeof(struct sync_file_info); + len = sizeof(struct sync_fence_info_data); - for (i = 0; i < sync_file->num_fences; ++i) { - struct fence *fence = sync_file->cbs[i].fence; + for (i = 0; i < fence->num_fences; ++i) { + struct fence *pt = fence->cbs[i].sync_pt; - ret = sync_fill_fence_info(fence, (u8 *)info + len, size - len); + ret = sync_fill_pt_info(pt, (u8 *)data + len, size - len); if (ret < 0) goto out; @@ -538,40 +690,43 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file, len += ret; } - info->len = len; + data->len = len; - if (copy_to_user((void __user *)arg, info, len)) + if (copy_to_user((void __user *)arg, data, len)) ret = -EFAULT; else ret = 0; out: - kfree(info); + kfree(data); return ret; } -static long sync_file_ioctl(struct file *file, unsigned int cmd, +static long sync_fence_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct sync_file *sync_file = file->private_data; + 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_file_ioctl_merge(sync_file, arg); + return sync_fence_ioctl_merge(fence, arg); case SYNC_IOC_FENCE_INFO: - return sync_file_ioctl_fence_info(sync_file, arg); + return sync_fence_ioctl_fence_info(fence, arg); default: return -ENOTTY; } } -static const struct file_operations sync_file_fops = { - .release = sync_file_release, - .poll = sync_file_poll, - .unlocked_ioctl = sync_file_ioctl, - .compat_ioctl = sync_file_ioctl, +static const struct file_operations sync_fence_fops = { + .release = sync_fence_release, + .poll = sync_fence_poll, + .unlocked_ioctl = sync_fence_ioctl, + .compat_ioctl = sync_fence_ioctl, }; diff --git a/drivers/staging/android/sync.h b/drivers/staging/android/sync.h index d2a1734..afa0752 100644 --- a/drivers/staging/android/sync.h +++ b/drivers/staging/android/sync.h @@ -18,35 +18,63 @@ #include #include #include +#include #include #include "uapi/sync.h" struct sync_timeline; -struct sync_file; +struct sync_pt; +struct sync_fence; /** * struct sync_timeline_ops - sync object implementation ops * @driver_name: name of the implementation + * @dup: duplicate a sync_pt * @has_signaled: returns: * 1 if pt has signaled * 0 if pt has not signaled * <0 on error + * @compare: returns: + * 1 if b will signal before a + * 0 if a and b will signal at the same time + * -1 if a will signal before b + * @free_pt: called before sync_pt is freed + * @release_obj: called before sync_timeline is freed + * @fill_driver_data: write implementation specific driver data to data. + * should return an error if there is not enough room + * as specified by size. This information is returned + * to userspace by SYNC_IOC_FENCE_INFO. * @timeline_value_str: fill str with the value of the sync_timeline's counter - * @fence_value_str: fill str with the value of the fence + * @pt_value_str: fill str with the value of the sync_pt */ struct sync_timeline_ops { const char *driver_name; /* required */ - int (*has_signaled)(struct fence *fence); + 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); + + /* optional */ + int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size); /* optional */ void (*timeline_value_str)(struct sync_timeline *timeline, char *str, int size); /* optional */ - void (*fence_value_str)(struct fence *fence, char *str, int size); + void (*pt_value_str)(struct sync_pt *pt, char *str, int size); }; /** @@ -57,7 +85,7 @@ struct sync_timeline_ops { * @destroyed: 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 - * fence.status + * sync_pt.status * @active_list_head: list of active (unsignaled/errored) sync_pts * @sync_timeline_list: membership in global sync_timeline_list */ @@ -80,44 +108,86 @@ struct sync_timeline { #endif }; -static inline struct sync_timeline *fence_parent(struct fence *fence) +/** + * struct sync_pt - sync point + * @fence: base fence class + * @child_list: membership in sync_timeline.child_list_head + * @active_list: membership in sync_timeline.active_list_head + * @signaled_list: membership in temporary signaled_list on stack + * @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 + * @timestamp: time which sync_pt status transitioned from active to + * signaled or error. + */ +struct sync_pt { + struct fence base; + + struct list_head child_list; + struct list_head active_list; +}; + +static inline struct sync_timeline *sync_pt_parent(struct sync_pt *pt) { - return container_of(fence->lock, struct sync_timeline, + return container_of(pt->base.lock, struct sync_timeline, child_list_lock); } -struct sync_file_cb { +struct sync_fence_cb { struct fence_cb cb; - struct fence *fence; - struct sync_file *sync_file; + struct fence *sync_pt; + struct sync_fence *fence; }; /** - * struct sync_file - sync file to export to the userspace + * struct sync_fence - sync fence * @file: file representing this fence * @kref: reference count on fence. - * @name: name of sync_file. Useful for debugging - * @sync_file_list: membership in global file list - * @num_fences number of sync_pts in the fence - * @wq: wait queue for fence signaling + * @name: name of sync_fence. Useful for debugging + * @pt_list_head: list of sync_pts in the fence. immutable once fence + * is created * @status: 0: signaled, >0:active, <0: error - * @cbs: sync_pts callback information + * + * @wq: wait queue for fence signaling + * @sync_fence_list: membership in global fence list */ -struct sync_file { +struct sync_fence { struct file *file; struct kref kref; char name[32]; #ifdef CONFIG_DEBUG_FS - struct list_head sync_file_list; + struct list_head sync_fence_list; #endif int num_fences; wait_queue_head_t wq; atomic_t status; - struct sync_file_cb cbs[]; + struct sync_fence_cb cbs[]; +}; + +struct sync_fence_waiter; +typedef void (*sync_callback_t)(struct sync_fence *fence, + struct sync_fence_waiter *waiter); + +/** + * 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 { + wait_queue_t work; + sync_callback_t callback; }; +static inline void sync_fence_waiter_init(struct sync_fence_waiter *waiter, + sync_callback_t callback) +{ + INIT_LIST_HEAD(&waiter->work.task_list); + waiter->callback = callback; +} + /* * API for sync_timeline implementers */ @@ -130,8 +200,7 @@ struct sync_file { * * Creates a new sync_timeline which will use the implementation specified by * @ops. @size bytes will be allocated allowing for implementation specific - * data to be kept after the generic sync_timeline struct. Returns the - * sync_timeline object or NULL in case of error. + * data to be kept after the generic sync_timeline struct. */ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, int size, const char *name); @@ -142,7 +211,7 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, * * A sync implementation should call this when the @obj is going away * (i.e. module unload.) @obj won't actually be freed until all its children - * fences are freed. + * sync_pts are freed. */ void sync_timeline_destroy(struct sync_timeline *obj); @@ -150,92 +219,148 @@ 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 implementation should call this any time one of it's fences + * A sync implementation 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: fence's parent sync_timeline + * @parent: sync_pt's parent sync_timeline * @size: size to allocate for this pt * - * Creates a new fence as a child of @parent. @size bytes will be + * 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 fence object or - * NULL in case of error. + * 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. */ -struct fence *sync_pt_create(struct sync_timeline *parent, int size); +void sync_pt_free(struct sync_pt *pt); /** * sync_fence_create() - creates a sync fence * @name: name of fence to create - * @fence: fence to add to the sync_fence + * @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); + +/** + * sync_fence_create_dma() - creates a sync fence from dma-fence + * @name: name of fence to create + * @pt: dma-fence to add to the fence * - * Creates a sync_file containg @fence. Once this is called, the sync_file - * takes ownership of @fence. + * Creates a fence containg @pt. Once this is called, the fence takes + * ownership of @pt. */ -struct sync_file *sync_file_create(const char *name, struct fence *fence); +struct sync_fence *sync_fence_create_dma(const char *name, struct fence *pt); /* - * API for sync_file consumers + * API for sync_fence consumers */ /** - * sync_file_merge() - merge two sync_files + * sync_fence_merge() - merge two fences * @name: name of new fence - * @a: sync_file a - * @b: sync_file b + * @a: fence a + * @b: fence b * - * Creates a new sync_file which contains copies of all the fences in both - * @a and @b. @a and @b remain valid, independent sync_file. Returns the - * new merged sync_file or NULL in case of error. + * 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_file *sync_file_merge(const char *name, - struct sync_file *a, struct sync_file *b); +struct sync_fence *sync_fence_merge(const char *name, + struct sync_fence *a, struct sync_fence *b); /** - * sync_file_fdget() - get a sync_file from an fd + * sync_fence_fdget() - get a fence from an fd * @fd: fd referencing a fence * - * Ensures @fd references a valid sync_file, increments the refcount of the - * backing file. Returns the sync_file or NULL in case of error. + * Ensures @fd references a valid fence, increments the refcount of the backing + * file, and returns the fence. */ -struct sync_file *sync_file_fdget(int fd); +struct sync_fence *sync_fence_fdget(int fd); /** - * sync_file_put() - puts a reference of a sync_file - * @sync_file: sync_file to put + * sync_fence_put() - puts a reference of a sync fence + * @fence: fence to put * - * Puts a reference on @sync_fence. If this is the last reference, the - * sync_fil and all it's sync_pts will be freed + * Puts a reference on @fence. If this is the last reference, the fence and + * all it's sync_pts will be freed */ -void sync_file_put(struct sync_file *sync_file); +void sync_fence_put(struct sync_fence *fence); /** - * sync_file_install() - installs a sync_file into a file descriptor - * @sync_file: sync_file to install + * sync_fence_install() - installs a fence into a file descriptor + * @fence: fence to install * @fd: file descriptor in which to install the fence * - * Installs @sync_file into @fd. @fd's should be acquired through + * Installs @fence into @fd. @fd's should be acquired through * get_unused_fd_flags(O_CLOEXEC). */ -void sync_file_install(struct sync_file *sync_file, int 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 + * @waiter: waiter callback struck + * + * Returns 1 if @fence has already signaled. + * + * Registers a callback to be called when @fence signals or has an error. + * @waiter should be initialized with sync_fence_waiter_init(). + */ +int sync_fence_wait_async(struct sync_fence *fence, + struct sync_fence_waiter *waiter); + +/** + * sync_fence_cancel_async() - cancels an async wait + * @fence: fence to wait on + * @waiter: waiter callback struck + * + * returns 0 if waiter was removed from fence's async waiter list. + * returns -ENOENT if waiter was not found on fence's async waiter list. + * + * Cancels a previously registered async wait. Will fail gracefully if + * @waiter was never registered or if @fence has already signaled @waiter. + */ +int sync_fence_cancel_async(struct sync_fence *fence, + struct sync_fence_waiter *waiter); + +/** + * 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 indefinitely + * if @timeout < 0 + */ +int sync_fence_wait(struct sync_fence *fence, long timeout); #ifdef CONFIG_DEBUG_FS void sync_timeline_debug_add(struct sync_timeline *obj); void sync_timeline_debug_remove(struct sync_timeline *obj); -void sync_file_debug_add(struct sync_file *fence); -void sync_file_debug_remove(struct sync_file *fence); +void sync_fence_debug_add(struct sync_fence *fence); +void sync_fence_debug_remove(struct sync_fence *fence); void sync_dump(void); #else # define sync_timeline_debug_add(obj) # define sync_timeline_debug_remove(obj) -# define sync_file_debug_add(fence) -# define sync_file_debug_remove(fence) +# define sync_fence_debug_add(fence) +# define sync_fence_debug_remove(fence) # define sync_dump() #endif +int sync_fence_wake_up_wq(wait_queue_t *curr, unsigned mode, + int wake_flags, void *key); #endif /* _LINUX_SYNC_H */ diff --git a/drivers/staging/android/sync_debug.c b/drivers/staging/android/sync_debug.c index 5a7ec58..f45d13c 100644 --- a/drivers/staging/android/sync_debug.c +++ b/drivers/staging/android/sync_debug.c @@ -26,16 +26,14 @@ #include #include #include -#include "sw_sync.h" +#include "sync.h" #ifdef CONFIG_DEBUG_FS -static struct dentry *dbgfs; - static LIST_HEAD(sync_timeline_list_head); static DEFINE_SPINLOCK(sync_timeline_list_lock); -static LIST_HEAD(sync_file_list_head); -static DEFINE_SPINLOCK(sync_file_list_lock); +static LIST_HEAD(sync_fence_list_head); +static DEFINE_SPINLOCK(sync_fence_list_lock); void sync_timeline_debug_add(struct sync_timeline *obj) { @@ -55,22 +53,22 @@ void sync_timeline_debug_remove(struct sync_timeline *obj) spin_unlock_irqrestore(&sync_timeline_list_lock, flags); } -void sync_file_debug_add(struct sync_file *sync_file) +void sync_fence_debug_add(struct sync_fence *fence) { unsigned long flags; - spin_lock_irqsave(&sync_file_list_lock, flags); - list_add_tail(&sync_file->sync_file_list, &sync_file_list_head); - spin_unlock_irqrestore(&sync_file_list_lock, flags); + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_add_tail(&fence->sync_fence_list, &sync_fence_list_head); + spin_unlock_irqrestore(&sync_fence_list_lock, flags); } -void sync_file_debug_remove(struct sync_file *sync_file) +void sync_fence_debug_remove(struct sync_fence *fence) { unsigned long flags; - spin_lock_irqsave(&sync_file_list_lock, flags); - list_del(&sync_file->sync_file_list); - spin_unlock_irqrestore(&sync_file_list_lock, flags); + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_del(&fence->sync_fence_list); + spin_unlock_irqrestore(&sync_fence_list_lock, flags); } static const char *sync_status_str(int status) @@ -84,40 +82,39 @@ static const char *sync_status_str(int status) return "error"; } -static void sync_print_fence(struct seq_file *s, struct fence *fence, bool show) +static void sync_print_pt(struct seq_file *s, struct fence *pt, bool fence) { int status = 1; - struct sync_timeline *parent = fence_parent(fence); - if (fence_is_signaled_locked(fence)) - status = fence->status; + if (fence_is_signaled_locked(pt)) + status = pt->status; - seq_printf(s, " %s%sfence %s", - show ? parent->name : "", - show ? "_" : "", + seq_printf(s, " %s%spt %s", + fence && pt->ops->get_timeline_name ? + pt->ops->get_timeline_name(pt) : "", + fence ? "_" : "", sync_status_str(status)); if (status <= 0) { struct timespec64 ts64 = - ktime_to_timespec64(fence->timestamp); + ktime_to_timespec64(pt->timestamp); seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec); } - if ((!fence || fence->ops->timeline_value_str) && - fence->ops->fence_value_str) { + if ((!fence || pt->ops->timeline_value_str) && + pt->ops->fence_value_str) { char value[64]; bool success; - fence->ops->fence_value_str(fence, value, sizeof(value)); + pt->ops->fence_value_str(pt, value, sizeof(value)); success = strlen(value); if (success) seq_printf(s, ": %s", value); if (success && fence) { - fence->ops->timeline_value_str(fence, value, - sizeof(value)); + pt->ops->timeline_value_str(pt, value, sizeof(value)); if (strlen(value)) seq_printf(s, " / %s", value); @@ -145,23 +142,38 @@ static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj) spin_lock_irqsave(&obj->child_list_lock, flags); list_for_each(pos, &obj->child_list_head) { - struct fence *fence = - container_of(pos, struct fence, child_list); - sync_print_fence(s, fence, false); + struct sync_pt *pt = + container_of(pos, struct sync_pt, child_list); + sync_print_pt(s, &pt->base, false); } spin_unlock_irqrestore(&obj->child_list_lock, flags); } -static void sync_print_sync_file(struct seq_file *s, - struct sync_file *sync_file) +static void sync_print_fence(struct seq_file *s, struct sync_fence *fence) { + wait_queue_t *pos; + unsigned long flags; int i; - seq_printf(s, "[%p] %s: %s\n", sync_file, sync_file->name, - sync_status_str(atomic_read(&sync_file->status))); + seq_printf(s, "[%p] %s: %s\n", fence, fence->name, + sync_status_str(atomic_read(&fence->status))); + + for (i = 0; i < fence->num_fences; ++i) { + sync_print_pt(s, fence->cbs[i].sync_pt, true); + } + + spin_lock_irqsave(&fence->wq.lock, flags); + list_for_each_entry(pos, &fence->wq.task_list, task_list) { + struct sync_fence_waiter *waiter; + + if (pos->func != &sync_fence_wake_up_wq) + continue; - for (i = 0; i < sync_file->num_fences; ++i) - sync_print_fence(s, sync_file->cbs[i].fence, true); + waiter = container_of(pos, struct sync_fence_waiter, work); + + seq_printf(s, "waiter %pF\n", waiter->callback); + } + spin_unlock_irqrestore(&fence->wq.lock, flags); } static int sync_debugfs_show(struct seq_file *s, void *unused) @@ -184,152 +196,33 @@ static int sync_debugfs_show(struct seq_file *s, void *unused) seq_puts(s, "fences:\n--------------\n"); - spin_lock_irqsave(&sync_file_list_lock, flags); - list_for_each(pos, &sync_file_list_head) { - struct sync_file *sync_file = - container_of(pos, struct sync_file, sync_file_list); + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_for_each(pos, &sync_fence_list_head) { + struct sync_fence *fence = + container_of(pos, struct sync_fence, sync_fence_list); - sync_print_sync_file(s, sync_file); + sync_print_fence(s, fence); seq_puts(s, "\n"); } - spin_unlock_irqrestore(&sync_file_list_lock, flags); + spin_unlock_irqrestore(&sync_fence_list_lock, flags); return 0; } -static int sync_info_debugfs_open(struct inode *inode, struct file *file) +static int sync_debugfs_open(struct inode *inode, struct file *file) { return single_open(file, sync_debugfs_show, inode->i_private); } -static const struct file_operations sync_info_debugfs_fops = { - .open = sync_info_debugfs_open, +static const struct file_operations sync_debugfs_fops = { + .open = sync_debugfs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -/* - * *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; - char task_comm[TASK_COMM_LEN]; - - get_task_comm(task_comm, current); - - obj = sw_sync_timeline_create(task_comm); - if (!obj) - return -ENOMEM; - - file->private_data = obj; - - return 0; -} - -static int sw_sync_debugfs_release(struct inode *inode, struct file *file) -{ - struct sw_sync_timeline *obj = file->private_data; - - sync_timeline_destroy(&obj->obj); - return 0; -} - -static long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, - unsigned long arg) -{ - int fd = get_unused_fd_flags(O_CLOEXEC); - int err; - struct fence *fence; - 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; - } - - fence = sw_sync_pt_create(obj, data.value); - if (!fence) { - err = -ENOMEM; - goto err; - } - - data.name[sizeof(data.name) - 1] = '\0'; - sync_file = sync_file_create(data.name, fence); - if (!sync_file) { - fence_put(fence); - err = -ENOMEM; - goto err; - } - - data.fence = fd; - if (copy_to_user((void __user *)arg, &data, sizeof(data))) { - sync_file_put(sync_file); - err = -EFAULT; - goto err; - } - - sync_file_install(sync_file, fd); - - return 0; - -err: - put_unused_fd(fd); - return err; -} - -static long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg) -{ - u32 value; - - if (copy_from_user(&value, (void __user *)arg, sizeof(value))) - return -EFAULT; - - sw_sync_timeline_inc(obj, value); - - return 0; -} - -static long sw_sync_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct sw_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; - } -} - -static 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, -}; - static __init int sync_debugfs_init(void) { - dbgfs = debugfs_create_dir("sync", NULL); - - debugfs_create_file("info", 0444, dbgfs, NULL, &sync_info_debugfs_fops); - debugfs_create_file("sw_sync", 0644, dbgfs, NULL, - &sw_sync_debugfs_fops); - + debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops); return 0; } late_initcall(sync_debugfs_init); diff --git a/drivers/staging/android/trace/sync.h b/drivers/staging/android/trace/sync.h index a0f80f4..77edb97 100644 --- a/drivers/staging/android/trace/sync.h +++ b/drivers/staging/android/trace/sync.h @@ -32,6 +32,50 @@ TRACE_EVENT(sync_timeline, TP_printk("name=%s value=%s", __get_str(name), __entry->value) ); +TRACE_EVENT(sync_wait, + TP_PROTO(struct sync_fence *fence, int begin), + + TP_ARGS(fence, begin), + + TP_STRUCT__entry( + __string(name, fence->name) + __field(s32, status) + __field(u32, begin) + ), + + TP_fast_assign( + __assign_str(name, fence->name); + __entry->status = atomic_read(&fence->status); + __entry->begin = begin; + ), + + TP_printk("%s name=%s state=%d", __entry->begin ? "begin" : "end", + __get_str(name), __entry->status) +); + +TRACE_EVENT(sync_pt, + TP_PROTO(struct fence *pt), + + TP_ARGS(pt), + + TP_STRUCT__entry( + __string(timeline, pt->ops->get_timeline_name(pt)) + __array(char, value, 32) + ), + + TP_fast_assign( + __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'; + } + ), + + TP_printk("name=%s value=%s", __get_str(timeline), __entry->value) +); + #endif /* if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) */ /* This part must be outside protection */ diff --git a/drivers/staging/android/uapi/sync.h b/drivers/staging/android/uapi/sync.h index a0cf357..e964c75 100644 --- a/drivers/staging/android/uapi/sync.h +++ b/drivers/staging/android/uapi/sync.h @@ -27,39 +27,51 @@ struct sync_merge_data { }; /** - * struct sync_fence_info - detailed fence information + * struct sync_pt_info - detailed sync_pt information + * @len: length of sync_pt_info including any driver_data * @obj_name: name of parent sync_timeline * @driver_name: name of driver implementing the parent - * @status: status of the fence 0:active 1:signaled <0:error + * @status: status of the sync_pt 0:active 1:signaled <0:error * @timestamp_ns: timestamp of status change in nanoseconds + * @driver_data: any driver dependent data */ -struct sync_fence_info { +struct sync_pt_info { + __u32 len; char obj_name[32]; char driver_name[32]; __s32 status; __u64 timestamp_ns; + + __u8 driver_data[0]; }; /** - * struct sync_file_info - data returned from fence info ioctl + * struct sync_fence_info_data - data returned from fence info ioctl * @len: ioctl caller writes the size of the buffer its passing in. - * ioctl returns length of sync_file_info returned to - * userspace including pt_info. + * ioctl returns length of sync_fence_data returned to userspace + * including pt_info. * @name: name of fence * @status: status of fence. 1: signaled 0:active <0:error - * @sync_fence_info: array of sync_fence_info for every fence in the sync_file + * @pt_info: a sync_pt_info struct for every sync_pt in the fence */ -struct sync_file_info { +struct sync_fence_info_data { __u32 len; char name[32]; __s32 status; - __u8 sync_fence_info[0]; + __u8 pt_info[0]; }; #define SYNC_IOC_MAGIC '>' /** + * DOC: SYNC_IOC_WAIT - wait for a fence to signal + * + * pass timeout in milliseconds. Waits indefinitely timeout < 0. + */ +#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32) + +/** * DOC: SYNC_IOC_MERGE - merge two fences * * Takes a struct sync_merge_data. Creates a new fence containing copies of @@ -71,14 +83,15 @@ struct sync_file_info { /** * DOC: SYNC_IOC_FENCE_INFO - get detailed information on a fence * - * Takes a struct sync_file_info_data with extra space allocated for pt_info. + * Takes a struct sync_fence_info_data with extra space allocated for pt_info. * Caller should write the size of the buffer into len. On return, len is - * updated to reflect the total size of the sync_file_info_data including + * updated to reflect the total size of the sync_fence_info_data including * pt_info. * * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence. * To iterate over the sync_pt_infos, use the sync_pt_info.len field. */ -#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2, struct sync_file_info) +#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\ + struct sync_fence_info_data) #endif /* _UAPI_LINUX_SYNC_H */