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