blob: 935769c754279e4e8c2367834dcbd0d197cc2a86 [file] [log] [blame]
Erik Gilling560b5462012-04-18 13:43:22 -07001/*
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>
Erik Gilling49795fb2012-05-16 13:14:43 -070018#include <linux/export.h>
Erik Gilling560b5462012-04-18 13:43:22 -070019#include <linux/file.h>
20#include <linux/fs.h>
21#include <linux/miscdevice.h>
22#include <linux/module.h>
23#include <linux/sw_sync.h>
24#include <linux/syscalls.h>
25#include <linux/uaccess.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}
Erik Gilling49795fb2012-05-16 13:14:43 -070046EXPORT_SYMBOL(sw_sync_pt_create);
Erik Gilling560b5462012-04-18 13:43:22 -070047
48static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt)
49{
50 struct sw_sync_pt *pt = (struct sw_sync_pt *) sync_pt;
51 struct sw_sync_timeline *obj =
52 (struct sw_sync_timeline *)sync_pt->parent;
53
54 return (struct sync_pt *) sw_sync_pt_create(obj, pt->value);
55}
56
57static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt)
58{
59 struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
60 struct sw_sync_timeline *obj =
61 (struct sw_sync_timeline *)sync_pt->parent;
62
63 return sw_sync_cmp(obj->value, pt->value) >= 0;
64}
65
66static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b)
67{
68 struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a;
69 struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b;
70
71 return sw_sync_cmp(pt_a->value, pt_b->value);
72}
73
Erik Gilling2c959d72012-03-15 14:23:23 -070074static void sw_sync_print_obj(struct seq_file *s,
75 struct sync_timeline *sync_timeline)
76{
77 struct sw_sync_timeline *obj = (struct sw_sync_timeline *)sync_timeline;
78
79 seq_printf(s, "%d", obj->value);
80}
81
82static void sw_sync_print_pt(struct seq_file *s, struct sync_pt *sync_pt)
83{
84 struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
85 struct sw_sync_timeline *obj =
86 (struct sw_sync_timeline *)sync_pt->parent;
87
88 seq_printf(s, "%d / %d", pt->value, obj->value);
89}
90
Erik Gilling484101e2012-03-15 17:46:07 -070091static int sw_sync_fill_driver_data(struct sync_pt *sync_pt,
92 void *data, int size)
93{
94 struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
95
96 if (size < sizeof(pt->value))
97 return -ENOMEM;
98
99 memcpy(data, &pt->value, sizeof(pt->value));
100
101 return sizeof(pt->value);
102}
103
Erik Gilling560b5462012-04-18 13:43:22 -0700104struct sync_timeline_ops sw_sync_timeline_ops = {
105 .driver_name = "sw_sync",
106 .dup = sw_sync_pt_dup,
107 .has_signaled = sw_sync_pt_has_signaled,
108 .compare = sw_sync_pt_compare,
Erik Gilling2c959d72012-03-15 14:23:23 -0700109 .print_obj = sw_sync_print_obj,
110 .print_pt = sw_sync_print_pt,
Erik Gilling484101e2012-03-15 17:46:07 -0700111 .fill_driver_data = sw_sync_fill_driver_data,
Erik Gilling560b5462012-04-18 13:43:22 -0700112};
113
114
115struct sw_sync_timeline *sw_sync_timeline_create(const char *name)
116{
117 struct sw_sync_timeline *obj = (struct sw_sync_timeline *)
118 sync_timeline_create(&sw_sync_timeline_ops,
119 sizeof(struct sw_sync_timeline),
120 name);
121
122 return obj;
123}
Erik Gilling49795fb2012-05-16 13:14:43 -0700124EXPORT_SYMBOL(sw_sync_timeline_create);
Erik Gilling560b5462012-04-18 13:43:22 -0700125
126void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc)
127{
128 obj->value += inc;
129
130 sync_timeline_signal(&obj->obj);
131}
Erik Gilling49795fb2012-05-16 13:14:43 -0700132EXPORT_SYMBOL(sw_sync_timeline_inc);
Erik Gilling560b5462012-04-18 13:43:22 -0700133
134#ifdef CONFIG_SW_SYNC_USER
135/* *WARNING*
136 *
137 * improper use of this can result in deadlocking kernel drivers from userspace.
138 */
139
140/* opening sw_sync create a new sync obj */
141int sw_sync_open(struct inode *inode, struct file *file)
142{
143 struct sw_sync_timeline *obj;
144 char task_comm[TASK_COMM_LEN];
145
146 get_task_comm(task_comm, current);
147
148 obj = sw_sync_timeline_create(task_comm);
149 if (obj == NULL)
150 return -ENOMEM;
151
152 file->private_data = obj;
153
154 return 0;
155}
156
157int sw_sync_release(struct inode *inode, struct file *file)
158{
159 struct sw_sync_timeline *obj = file->private_data;
160 sync_timeline_destroy(&obj->obj);
161 return 0;
162}
163
164long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, unsigned long arg)
165{
166 int fd = get_unused_fd();
167 int err;
168 struct sync_pt *pt;
169 struct sync_fence *fence;
170 struct sw_sync_create_fence_data data;
171
172 if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
173 return -EFAULT;
174
175 pt = sw_sync_pt_create(obj, data.value);
176 if (pt == NULL) {
177 err = -ENOMEM;
178 goto err;
179 }
180
181 data.name[sizeof(data.name) - 1] = '\0';
182 fence = sync_fence_create(data.name, pt);
183 if (fence == NULL) {
184 sync_pt_free(pt);
185 err = -ENOMEM;
186 goto err;
187 }
188
189 data.fence = fd;
190 if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
191 sync_fence_put(fence);
192 err = -EFAULT;
193 goto err;
194 }
195
196 sync_fence_install(fence, fd);
197
198 return 0;
199
200err:
201 put_unused_fd(fd);
202 return err;
203}
204
205long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg)
206{
207 u32 value;
208
209 if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
210 return -EFAULT;
211
212 sw_sync_timeline_inc(obj, value);
213
214 return 0;
215}
216
217long sw_sync_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
218{
219 struct sw_sync_timeline *obj = file->private_data;
220
221 switch (cmd) {
222 case SW_SYNC_IOC_CREATE_FENCE:
223 return sw_sync_ioctl_create_fence(obj, arg);
224
225 case SW_SYNC_IOC_INC:
226 return sw_sync_ioctl_inc(obj, arg);
227
228 default:
229 return -ENOTTY;
230 }
231}
232
233static const struct file_operations sw_sync_fops = {
234 .owner = THIS_MODULE,
235 .open = sw_sync_open,
236 .release = sw_sync_release,
237 .unlocked_ioctl = sw_sync_ioctl,
238};
239
240static struct miscdevice sw_sync_dev = {
241 .minor = MISC_DYNAMIC_MINOR,
242 .name = "sw_sync",
243 .fops = &sw_sync_fops,
244};
245
246int __init sw_sync_device_init(void)
247{
248 return misc_register(&sw_sync_dev);
249}
250
251void __exit sw_sync_device_remove(void)
252{
253 misc_deregister(&sw_sync_dev);
254}
255
256module_init(sw_sync_device_init);
257module_exit(sw_sync_device_remove);
258
259#endif /* CONFIG_SW_SYNC_USER */