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