18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Sync File validation framework 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Google, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/file.h> 98c2ecf20Sopenharmony_ci#include <linux/fs.h> 108c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/sync_file.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "sync_debug.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 178c2ecf20Sopenharmony_ci#include "sync_trace.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * SW SYNC validation framework 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * A sync object driver that uses a 32bit counter to coordinate 238c2ecf20Sopenharmony_ci * synchronization. Useful when there is no hardware primitive backing 248c2ecf20Sopenharmony_ci * the synchronization. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * To start the framework just open: 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * <debugfs>/sync/sw_sync 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * That will create a sync timeline, all fences created under this timeline 318c2ecf20Sopenharmony_ci * file descriptor will belong to the this timeline. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * The 'sw_sync' file can be opened many times as to create different 348c2ecf20Sopenharmony_ci * timelines. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * Fences can be created with SW_SYNC_IOC_CREATE_FENCE ioctl with struct 378c2ecf20Sopenharmony_ci * sw_sync_create_fence_data as parameter. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * To increment the timeline counter, SW_SYNC_IOC_INC ioctl should be used 408c2ecf20Sopenharmony_ci * with the increment as u32. This will update the last signaled value 418c2ecf20Sopenharmony_ci * from the timeline and signal any fence that has a seqno smaller or equal 428c2ecf20Sopenharmony_ci * to it. 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * struct sw_sync_create_fence_data 458c2ecf20Sopenharmony_ci * @value: the seqno to initialise the fence with 468c2ecf20Sopenharmony_ci * @name: the name of the new sync point 478c2ecf20Sopenharmony_ci * @fence: return the fd of the new sync_file with the created fence 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_cistruct sw_sync_create_fence_data { 508c2ecf20Sopenharmony_ci __u32 value; 518c2ecf20Sopenharmony_ci char name[32]; 528c2ecf20Sopenharmony_ci __s32 fence; /* fd of new fence */ 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define SW_SYNC_IOC_MAGIC 'W' 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ 588c2ecf20Sopenharmony_ci struct sw_sync_create_fence_data) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic const struct dma_fence_ops timeline_fence_ops; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic inline struct sync_pt *dma_fence_to_sync_pt(struct dma_fence *fence) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci if (fence->ops != &timeline_fence_ops) 678c2ecf20Sopenharmony_ci return NULL; 688c2ecf20Sopenharmony_ci return container_of(fence, struct sync_pt, base); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/** 728c2ecf20Sopenharmony_ci * sync_timeline_create() - creates a sync object 738c2ecf20Sopenharmony_ci * @name: sync_timeline name 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * Creates a new sync_timeline. Returns the sync_timeline object or NULL in 768c2ecf20Sopenharmony_ci * case of error. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistatic struct sync_timeline *sync_timeline_create(const char *name) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct sync_timeline *obj; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci obj = kzalloc(sizeof(*obj), GFP_KERNEL); 838c2ecf20Sopenharmony_ci if (!obj) 848c2ecf20Sopenharmony_ci return NULL; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci kref_init(&obj->kref); 878c2ecf20Sopenharmony_ci obj->context = dma_fence_context_alloc(1); 888c2ecf20Sopenharmony_ci strlcpy(obj->name, name, sizeof(obj->name)); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci obj->pt_tree = RB_ROOT; 918c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&obj->pt_list); 928c2ecf20Sopenharmony_ci spin_lock_init(&obj->lock); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci sync_timeline_debug_add(obj); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return obj; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void sync_timeline_free(struct kref *kref) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct sync_timeline *obj = 1028c2ecf20Sopenharmony_ci container_of(kref, struct sync_timeline, kref); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci sync_timeline_debug_remove(obj); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci kfree(obj); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void sync_timeline_get(struct sync_timeline *obj) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci kref_get(&obj->kref); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void sync_timeline_put(struct sync_timeline *obj) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci kref_put(&obj->kref, sync_timeline_free); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic const char *timeline_fence_get_driver_name(struct dma_fence *fence) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci return "sw_sync"; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic const char *timeline_fence_get_timeline_name(struct dma_fence *fence) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct sync_timeline *parent = dma_fence_parent(fence); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return parent->name; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void timeline_fence_release(struct dma_fence *fence) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct sync_pt *pt = dma_fence_to_sync_pt(fence); 1348c2ecf20Sopenharmony_ci struct sync_timeline *parent = dma_fence_parent(fence); 1358c2ecf20Sopenharmony_ci unsigned long flags; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci spin_lock_irqsave(fence->lock, flags); 1388c2ecf20Sopenharmony_ci if (!list_empty(&pt->link)) { 1398c2ecf20Sopenharmony_ci list_del(&pt->link); 1408c2ecf20Sopenharmony_ci rb_erase(&pt->node, &parent->pt_tree); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(fence->lock, flags); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci sync_timeline_put(parent); 1458c2ecf20Sopenharmony_ci dma_fence_free(fence); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic bool timeline_fence_signaled(struct dma_fence *fence) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct sync_timeline *parent = dma_fence_parent(fence); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return !__dma_fence_is_later(fence->seqno, parent->value, fence->ops); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic bool timeline_fence_enable_signaling(struct dma_fence *fence) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci return true; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void timeline_fence_value_str(struct dma_fence *fence, 1618c2ecf20Sopenharmony_ci char *str, int size) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci snprintf(str, size, "%lld", fence->seqno); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void timeline_fence_timeline_value_str(struct dma_fence *fence, 1678c2ecf20Sopenharmony_ci char *str, int size) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct sync_timeline *parent = dma_fence_parent(fence); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci snprintf(str, size, "%d", parent->value); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const struct dma_fence_ops timeline_fence_ops = { 1758c2ecf20Sopenharmony_ci .get_driver_name = timeline_fence_get_driver_name, 1768c2ecf20Sopenharmony_ci .get_timeline_name = timeline_fence_get_timeline_name, 1778c2ecf20Sopenharmony_ci .enable_signaling = timeline_fence_enable_signaling, 1788c2ecf20Sopenharmony_ci .signaled = timeline_fence_signaled, 1798c2ecf20Sopenharmony_ci .release = timeline_fence_release, 1808c2ecf20Sopenharmony_ci .fence_value_str = timeline_fence_value_str, 1818c2ecf20Sopenharmony_ci .timeline_value_str = timeline_fence_timeline_value_str, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/** 1858c2ecf20Sopenharmony_ci * sync_timeline_signal() - signal a status change on a sync_timeline 1868c2ecf20Sopenharmony_ci * @obj: sync_timeline to signal 1878c2ecf20Sopenharmony_ci * @inc: num to increment on timeline->value 1888c2ecf20Sopenharmony_ci * 1898c2ecf20Sopenharmony_ci * A sync implementation should call this any time one of it's fences 1908c2ecf20Sopenharmony_ci * has signaled or has an error condition. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci LIST_HEAD(signalled); 1958c2ecf20Sopenharmony_ci struct sync_pt *pt, *next; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci trace_sync_timeline(obj); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci spin_lock_irq(&obj->lock); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci obj->value += inc; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci list_for_each_entry_safe(pt, next, &obj->pt_list, link) { 2048c2ecf20Sopenharmony_ci if (!timeline_fence_signaled(&pt->base)) 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci dma_fence_get(&pt->base); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci list_move_tail(&pt->link, &signalled); 2108c2ecf20Sopenharmony_ci rb_erase(&pt->node, &obj->pt_tree); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci dma_fence_signal_locked(&pt->base); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci spin_unlock_irq(&obj->lock); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci list_for_each_entry_safe(pt, next, &signalled, link) { 2188c2ecf20Sopenharmony_ci list_del_init(&pt->link); 2198c2ecf20Sopenharmony_ci dma_fence_put(&pt->base); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/** 2248c2ecf20Sopenharmony_ci * sync_pt_create() - creates a sync pt 2258c2ecf20Sopenharmony_ci * @obj: parent sync_timeline 2268c2ecf20Sopenharmony_ci * @value: value of the fence 2278c2ecf20Sopenharmony_ci * 2288c2ecf20Sopenharmony_ci * Creates a new sync_pt (fence) as a child of @parent. @size bytes will be 2298c2ecf20Sopenharmony_ci * allocated allowing for implementation specific data to be kept after 2308c2ecf20Sopenharmony_ci * the generic sync_timeline struct. Returns the sync_pt object or 2318c2ecf20Sopenharmony_ci * NULL in case of error. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_cistatic struct sync_pt *sync_pt_create(struct sync_timeline *obj, 2348c2ecf20Sopenharmony_ci unsigned int value) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct sync_pt *pt; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci pt = kzalloc(sizeof(*pt), GFP_KERNEL); 2398c2ecf20Sopenharmony_ci if (!pt) 2408c2ecf20Sopenharmony_ci return NULL; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci sync_timeline_get(obj); 2438c2ecf20Sopenharmony_ci dma_fence_init(&pt->base, &timeline_fence_ops, &obj->lock, 2448c2ecf20Sopenharmony_ci obj->context, value); 2458c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pt->link); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci spin_lock_irq(&obj->lock); 2488c2ecf20Sopenharmony_ci if (!dma_fence_is_signaled_locked(&pt->base)) { 2498c2ecf20Sopenharmony_ci struct rb_node **p = &obj->pt_tree.rb_node; 2508c2ecf20Sopenharmony_ci struct rb_node *parent = NULL; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci while (*p) { 2538c2ecf20Sopenharmony_ci struct sync_pt *other; 2548c2ecf20Sopenharmony_ci int cmp; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci parent = *p; 2578c2ecf20Sopenharmony_ci other = rb_entry(parent, typeof(*pt), node); 2588c2ecf20Sopenharmony_ci cmp = value - other->base.seqno; 2598c2ecf20Sopenharmony_ci if (cmp > 0) { 2608c2ecf20Sopenharmony_ci p = &parent->rb_right; 2618c2ecf20Sopenharmony_ci } else if (cmp < 0) { 2628c2ecf20Sopenharmony_ci p = &parent->rb_left; 2638c2ecf20Sopenharmony_ci } else { 2648c2ecf20Sopenharmony_ci if (dma_fence_get_rcu(&other->base)) { 2658c2ecf20Sopenharmony_ci sync_timeline_put(obj); 2668c2ecf20Sopenharmony_ci kfree(pt); 2678c2ecf20Sopenharmony_ci pt = other; 2688c2ecf20Sopenharmony_ci goto unlock; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci p = &parent->rb_left; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci rb_link_node(&pt->node, parent, p); 2748c2ecf20Sopenharmony_ci rb_insert_color(&pt->node, &obj->pt_tree); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci parent = rb_next(&pt->node); 2778c2ecf20Sopenharmony_ci list_add_tail(&pt->link, 2788c2ecf20Sopenharmony_ci parent ? &rb_entry(parent, typeof(*pt), node)->link : &obj->pt_list); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ciunlock: 2818c2ecf20Sopenharmony_ci spin_unlock_irq(&obj->lock); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return pt; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/* 2878c2ecf20Sopenharmony_ci * *WARNING* 2888c2ecf20Sopenharmony_ci * 2898c2ecf20Sopenharmony_ci * improper use of this can result in deadlocking kernel drivers from userspace. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* opening sw_sync create a new sync obj */ 2938c2ecf20Sopenharmony_cistatic int sw_sync_debugfs_open(struct inode *inode, struct file *file) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct sync_timeline *obj; 2968c2ecf20Sopenharmony_ci char task_comm[TASK_COMM_LEN]; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci get_task_comm(task_comm, current); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci obj = sync_timeline_create(task_comm); 3018c2ecf20Sopenharmony_ci if (!obj) 3028c2ecf20Sopenharmony_ci return -ENOMEM; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci file->private_data = obj; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int sw_sync_debugfs_release(struct inode *inode, struct file *file) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct sync_timeline *obj = file->private_data; 3128c2ecf20Sopenharmony_ci struct sync_pt *pt, *next; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci spin_lock_irq(&obj->lock); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci list_for_each_entry_safe(pt, next, &obj->pt_list, link) { 3178c2ecf20Sopenharmony_ci dma_fence_set_error(&pt->base, -ENOENT); 3188c2ecf20Sopenharmony_ci dma_fence_signal_locked(&pt->base); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci spin_unlock_irq(&obj->lock); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci sync_timeline_put(obj); 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic long sw_sync_ioctl_create_fence(struct sync_timeline *obj, 3288c2ecf20Sopenharmony_ci unsigned long arg) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci int fd = get_unused_fd_flags(O_CLOEXEC); 3318c2ecf20Sopenharmony_ci int err; 3328c2ecf20Sopenharmony_ci struct sync_pt *pt; 3338c2ecf20Sopenharmony_ci struct sync_file *sync_file; 3348c2ecf20Sopenharmony_ci struct sw_sync_create_fence_data data; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (fd < 0) 3378c2ecf20Sopenharmony_ci return fd; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { 3408c2ecf20Sopenharmony_ci err = -EFAULT; 3418c2ecf20Sopenharmony_ci goto err; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci pt = sync_pt_create(obj, data.value); 3458c2ecf20Sopenharmony_ci if (!pt) { 3468c2ecf20Sopenharmony_ci err = -ENOMEM; 3478c2ecf20Sopenharmony_ci goto err; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci sync_file = sync_file_create(&pt->base); 3518c2ecf20Sopenharmony_ci dma_fence_put(&pt->base); 3528c2ecf20Sopenharmony_ci if (!sync_file) { 3538c2ecf20Sopenharmony_ci err = -ENOMEM; 3548c2ecf20Sopenharmony_ci goto err; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci data.fence = fd; 3588c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &data, sizeof(data))) { 3598c2ecf20Sopenharmony_ci fput(sync_file->file); 3608c2ecf20Sopenharmony_ci err = -EFAULT; 3618c2ecf20Sopenharmony_ci goto err; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci fd_install(fd, sync_file->file); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cierr: 3698c2ecf20Sopenharmony_ci put_unused_fd(fd); 3708c2ecf20Sopenharmony_ci return err; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic long sw_sync_ioctl_inc(struct sync_timeline *obj, unsigned long arg) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci u32 value; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (copy_from_user(&value, (void __user *)arg, sizeof(value))) 3788c2ecf20Sopenharmony_ci return -EFAULT; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci while (value > INT_MAX) { 3818c2ecf20Sopenharmony_ci sync_timeline_signal(obj, INT_MAX); 3828c2ecf20Sopenharmony_ci value -= INT_MAX; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci sync_timeline_signal(obj, value); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic long sw_sync_ioctl(struct file *file, unsigned int cmd, 3918c2ecf20Sopenharmony_ci unsigned long arg) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct sync_timeline *obj = file->private_data; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci switch (cmd) { 3968c2ecf20Sopenharmony_ci case SW_SYNC_IOC_CREATE_FENCE: 3978c2ecf20Sopenharmony_ci return sw_sync_ioctl_create_fence(obj, arg); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci case SW_SYNC_IOC_INC: 4008c2ecf20Sopenharmony_ci return sw_sync_ioctl_inc(obj, arg); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci default: 4038c2ecf20Sopenharmony_ci return -ENOTTY; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ciconst struct file_operations sw_sync_debugfs_fops = { 4088c2ecf20Sopenharmony_ci .open = sw_sync_debugfs_open, 4098c2ecf20Sopenharmony_ci .release = sw_sync_debugfs_release, 4108c2ecf20Sopenharmony_ci .unlocked_ioctl = sw_sync_ioctl, 4118c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 4128c2ecf20Sopenharmony_ci}; 413