sw_sync.c revision 9d1906e61dda982070e3910a04d9cce050f7f1a4
1/* 2 * drivers/base/sw_sync.c 3 * 4 * Copyright (C) 2012 Google, Inc. 5 * 6 * This software is licensed under the terms of the GNU General Public 7 * License version 2, as published by the Free Software Foundation, and 8 * may be copied, distributed, and modified under those terms. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 */ 16 17#include <linux/kernel.h> 18#include <linux/file.h> 19#include <linux/fs.h> 20#include <linux/miscdevice.h> 21#include <linux/module.h> 22#include <linux/syscalls.h> 23#include <linux/uaccess.h> 24 25#include "sw_sync.h" 26 27static int sw_sync_cmp(u32 a, u32 b) 28{ 29 if (a == b) 30 return 0; 31 32 return ((s32)a - (s32)b) < 0 ? -1 : 1; 33} 34 35struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) 36{ 37 struct sw_sync_pt *pt; 38 39 pt = (struct sw_sync_pt *) 40 sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt)); 41 42 pt->value = value; 43 44 return (struct sync_pt *)pt; 45} 46 47static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt) 48{ 49 struct sw_sync_pt *pt = (struct sw_sync_pt *) sync_pt; 50 struct sw_sync_timeline *obj = 51 (struct sw_sync_timeline *)sync_pt->parent; 52 53 return (struct sync_pt *) sw_sync_pt_create(obj, pt->value); 54} 55 56static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt) 57{ 58 struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; 59 struct sw_sync_timeline *obj = 60 (struct sw_sync_timeline *)sync_pt->parent; 61 62 return sw_sync_cmp(obj->value, pt->value) >= 0; 63} 64 65static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b) 66{ 67 struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a; 68 struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b; 69 70 return sw_sync_cmp(pt_a->value, pt_b->value); 71} 72 73struct sync_timeline_ops sw_sync_timeline_ops = { 74 .driver_name = "sw_sync", 75 .dup = sw_sync_pt_dup, 76 .has_signaled = sw_sync_pt_has_signaled, 77 .compare = sw_sync_pt_compare, 78}; 79 80 81struct sw_sync_timeline *sw_sync_timeline_create(const char *name) 82{ 83 struct sw_sync_timeline *obj = (struct sw_sync_timeline *) 84 sync_timeline_create(&sw_sync_timeline_ops, 85 sizeof(struct sw_sync_timeline), 86 name); 87 88 return obj; 89} 90 91void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) 92{ 93 obj->value += inc; 94 95 sync_timeline_signal(&obj->obj); 96} 97 98 99#ifdef CONFIG_SW_SYNC_USER 100/* *WARNING* 101 * 102 * improper use of this can result in deadlocking kernel drivers from userspace. 103 */ 104 105/* opening sw_sync create a new sync obj */ 106int sw_sync_open(struct inode *inode, struct file *file) 107{ 108 struct sw_sync_timeline *obj; 109 char task_comm[TASK_COMM_LEN]; 110 111 get_task_comm(task_comm, current); 112 113 obj = sw_sync_timeline_create(task_comm); 114 if (obj == NULL) 115 return -ENOMEM; 116 117 file->private_data = obj; 118 119 return 0; 120} 121 122int sw_sync_release(struct inode *inode, struct file *file) 123{ 124 struct sw_sync_timeline *obj = file->private_data; 125 sync_timeline_destroy(&obj->obj); 126 return 0; 127} 128 129long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, unsigned long arg) 130{ 131 int fd = get_unused_fd(); 132 int err; 133 struct sync_pt *pt; 134 struct sync_fence *fence; 135 struct sw_sync_create_fence_data data; 136 137 if (copy_from_user(&data, (void __user *)arg, sizeof(data))) 138 return -EFAULT; 139 140 pt = sw_sync_pt_create(obj, data.value); 141 if (pt == NULL) { 142 err = -ENOMEM; 143 goto err; 144 } 145 146 data.name[sizeof(data.name) - 1] = '\0'; 147 fence = sync_fence_create(data.name, pt); 148 if (fence == NULL) { 149 sync_pt_free(pt); 150 err = -ENOMEM; 151 goto err; 152 } 153 154 data.fence = fd; 155 if (copy_to_user((void __user *)arg, &data, sizeof(data))) { 156 sync_fence_put(fence); 157 err = -EFAULT; 158 goto err; 159 } 160 161 sync_fence_install(fence, fd); 162 163 return 0; 164 165err: 166 put_unused_fd(fd); 167 return err; 168} 169 170long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg) 171{ 172 u32 value; 173 174 if (copy_from_user(&value, (void __user *)arg, sizeof(value))) 175 return -EFAULT; 176 177 sw_sync_timeline_inc(obj, value); 178 179 return 0; 180} 181 182long sw_sync_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 183{ 184 struct sw_sync_timeline *obj = file->private_data; 185 186 switch (cmd) { 187 case SW_SYNC_IOC_CREATE_FENCE: 188 return sw_sync_ioctl_create_fence(obj, arg); 189 190 case SW_SYNC_IOC_INC: 191 return sw_sync_ioctl_inc(obj, arg); 192 193 default: 194 return -ENOTTY; 195 } 196} 197 198static const struct file_operations sw_sync_fops = { 199 .owner = THIS_MODULE, 200 .open = sw_sync_open, 201 .release = sw_sync_release, 202 .unlocked_ioctl = sw_sync_ioctl, 203}; 204 205static struct miscdevice sw_sync_dev = { 206 .minor = MISC_DYNAMIC_MINOR, 207 .name = "sw_sync", 208 .fops = &sw_sync_fops, 209}; 210 211int __init sw_sync_device_init(void) 212{ 213 return misc_register(&sw_sync_dev); 214} 215 216void __exit sw_sync_device_remove(void) 217{ 218 misc_deregister(&sw_sync_dev); 219} 220 221module_init(sw_sync_device_init); 222module_exit(sw_sync_device_remove); 223 224#endif /* CONFIG_SW_SYNC_USER */ 225