sync.c revision 7ad530bf2499c702a6dcbb279cf78b76845ed584
1/*
2 * drivers/base/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/file.h>
18#include <linux/fs.h>
19#include <linux/kernel.h>
20#include <linux/sched.h>
21#include <linux/slab.h>
22#include <linux/uaccess.h>
23#include <linux/anon_inodes.h>
24
25#include "sync.h"
26
27static void sync_fence_signal_pt(struct sync_pt *pt);
28static int _sync_pt_has_signaled(struct sync_pt *pt);
29
30struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
31					   int size, const char *name)
32{
33	struct sync_timeline *obj;
34
35	if (size < sizeof(struct sync_timeline))
36		return NULL;
37
38	obj = kzalloc(size, GFP_KERNEL);
39	if (obj == NULL)
40		return NULL;
41
42	obj->ops = ops;
43	strlcpy(obj->name, name, sizeof(obj->name));
44
45	INIT_LIST_HEAD(&obj->child_list_head);
46	spin_lock_init(&obj->child_list_lock);
47
48	INIT_LIST_HEAD(&obj->active_list_head);
49	spin_lock_init(&obj->active_list_lock);
50
51	return obj;
52}
53
54void sync_timeline_destroy(struct sync_timeline *obj)
55{
56	unsigned long flags;
57	bool needs_freeing;
58
59	spin_lock_irqsave(&obj->child_list_lock, flags);
60	obj->destroyed = true;
61	needs_freeing = list_empty(&obj->child_list_head);
62	spin_unlock_irqrestore(&obj->child_list_lock, flags);
63
64	if (needs_freeing)
65		kfree(obj);
66	else
67		sync_timeline_signal(obj);
68}
69
70static void sync_timeline_add_pt(struct sync_timeline *obj, struct sync_pt *pt)
71{
72	unsigned long flags;
73
74	pt->parent = obj;
75
76	spin_lock_irqsave(&obj->child_list_lock, flags);
77	list_add_tail(&pt->child_list, &obj->child_list_head);
78	spin_unlock_irqrestore(&obj->child_list_lock, flags);
79}
80
81static void sync_timeline_remove_pt(struct sync_pt *pt)
82{
83	struct sync_timeline *obj = pt->parent;
84	unsigned long flags;
85	bool needs_freeing;
86
87	spin_lock_irqsave(&obj->active_list_lock, flags);
88	if (!list_empty(&pt->active_list))
89		list_del_init(&pt->active_list);
90	spin_unlock_irqrestore(&obj->active_list_lock, flags);
91
92	spin_lock_irqsave(&obj->child_list_lock, flags);
93	list_del(&pt->child_list);
94	needs_freeing = obj->destroyed && list_empty(&obj->child_list_head);
95	spin_unlock_irqrestore(&obj->child_list_lock, flags);
96
97	if (needs_freeing)
98		kfree(obj);
99}
100
101void sync_timeline_signal(struct sync_timeline *obj)
102{
103	unsigned long flags;
104	LIST_HEAD(signaled_pts);
105	struct list_head *pos, *n;
106
107	spin_lock_irqsave(&obj->active_list_lock, flags);
108
109	list_for_each_safe(pos, n, &obj->active_list_head) {
110		struct sync_pt *pt =
111			container_of(pos, struct sync_pt, active_list);
112
113		if (_sync_pt_has_signaled(pt))
114			list_move(pos, &signaled_pts);
115	}
116
117	spin_unlock_irqrestore(&obj->active_list_lock, flags);
118
119	list_for_each_safe(pos, n, &signaled_pts) {
120		struct sync_pt *pt =
121			container_of(pos, struct sync_pt, active_list);
122
123		list_del_init(pos);
124		sync_fence_signal_pt(pt);
125	}
126}
127
128struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size)
129{
130	struct sync_pt *pt;
131
132	if (size < sizeof(struct sync_pt))
133		return NULL;
134
135	pt = kzalloc(size, GFP_KERNEL);
136	if (pt == NULL)
137		return NULL;
138
139	INIT_LIST_HEAD(&pt->active_list);
140	sync_timeline_add_pt(parent, pt);
141
142	return pt;
143}
144
145void sync_pt_free(struct sync_pt *pt)
146{
147	if (pt->parent->ops->free_pt)
148		pt->parent->ops->free_pt(pt);
149
150	sync_timeline_remove_pt(pt);
151
152	kfree(pt);
153}
154
155/* call with pt->parent->active_list_lock held */
156static int _sync_pt_has_signaled(struct sync_pt *pt)
157{
158	if (!pt->status)
159		pt->status = pt->parent->ops->has_signaled(pt);
160
161	if (!pt->status && pt->parent->destroyed)
162		pt->status = -ENOENT;
163
164	return pt->status;
165}
166
167static struct sync_pt *sync_pt_dup(struct sync_pt *pt)
168{
169	return pt->parent->ops->dup(pt);
170}
171
172/* Adds a sync pt to the active queue.  Called when added to a fence */
173static void sync_pt_activate(struct sync_pt *pt)
174{
175	struct sync_timeline *obj = pt->parent;
176	unsigned long flags;
177	int err;
178
179	spin_lock_irqsave(&obj->active_list_lock, flags);
180
181	err = _sync_pt_has_signaled(pt);
182	if (err != 0)
183		goto out;
184
185	list_add_tail(&pt->active_list, &obj->active_list_head);
186
187out:
188	spin_unlock_irqrestore(&obj->active_list_lock, flags);
189}
190
191static int sync_fence_release(struct inode *inode, struct file *file);
192static long sync_fence_ioctl(struct file *file, unsigned int cmd,
193			     unsigned long arg);
194
195
196static const struct file_operations sync_fence_fops = {
197	.release = sync_fence_release,
198	.unlocked_ioctl = sync_fence_ioctl,
199};
200
201static struct sync_fence *sync_fence_alloc(const char *name)
202{
203	struct sync_fence *fence;
204
205	fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL);
206	if (fence == NULL)
207		return NULL;
208
209	fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops,
210					 fence, 0);
211	if (fence->file == NULL)
212		goto err;
213
214	strlcpy(fence->name, name, sizeof(fence->name));
215
216	INIT_LIST_HEAD(&fence->pt_list_head);
217	INIT_LIST_HEAD(&fence->waiter_list_head);
218	spin_lock_init(&fence->waiter_list_lock);
219
220	init_waitqueue_head(&fence->wq);
221	return fence;
222
223err:
224	kfree(fence);
225	return NULL;
226}
227
228/* TODO: implement a create which takes more that one sync_pt */
229struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)
230{
231	struct sync_fence *fence;
232
233	if (pt->fence)
234		return NULL;
235
236	fence = sync_fence_alloc(name);
237	if (fence == NULL)
238		return NULL;
239
240	pt->fence = fence;
241	list_add(&pt->pt_list, &fence->pt_list_head);
242	sync_pt_activate(pt);
243
244	return fence;
245}
246
247static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src)
248{
249	struct list_head *pos;
250
251	list_for_each(pos, &src->pt_list_head) {
252		struct sync_pt *orig_pt =
253			container_of(pos, struct sync_pt, pt_list);
254		struct sync_pt *new_pt = sync_pt_dup(orig_pt);
255
256		if (new_pt == NULL)
257			return -ENOMEM;
258
259		new_pt->fence = dst;
260		list_add(&new_pt->pt_list, &dst->pt_list_head);
261		sync_pt_activate(new_pt);
262	}
263
264	return 0;
265}
266
267static void sync_fence_free_pts(struct sync_fence *fence)
268{
269	struct list_head *pos, *n;
270
271	list_for_each_safe(pos, n, &fence->pt_list_head) {
272		struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
273		sync_pt_free(pt);
274	}
275}
276
277struct sync_fence *sync_fence_fdget(int fd)
278{
279	struct file *file = fget(fd);
280
281	if (file == NULL)
282		return NULL;
283
284	if (file->f_op != &sync_fence_fops)
285		goto err;
286
287	return file->private_data;
288
289err:
290	fput(file);
291	return NULL;
292}
293
294void sync_fence_put(struct sync_fence *fence)
295{
296	fput(fence->file);
297}
298
299void sync_fence_install(struct sync_fence *fence, int fd)
300{
301	fd_install(fd, fence->file);
302}
303
304static int sync_fence_get_status(struct sync_fence *fence)
305{
306	struct list_head *pos;
307	int status = 1;
308
309	list_for_each(pos, &fence->pt_list_head) {
310		struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
311		int pt_status = pt->status;
312
313		if (pt_status < 0) {
314			status = pt_status;
315			break;
316		} else if (status == 1) {
317			status = pt_status;
318		}
319	}
320
321	return status;
322}
323
324struct sync_fence *sync_fence_merge(const char *name,
325				    struct sync_fence *a, struct sync_fence *b)
326{
327	struct sync_fence *fence;
328	int err;
329
330	fence = sync_fence_alloc(name);
331	if (fence == NULL)
332		return NULL;
333
334	err = sync_fence_copy_pts(fence, a);
335	if (err < 0)
336		goto err;
337
338	err = sync_fence_copy_pts(fence, b);
339	if (err < 0)
340		goto err;
341
342	fence->status = sync_fence_get_status(fence);
343
344	return fence;
345err:
346	sync_fence_free_pts(fence);
347	kfree(fence);
348	return NULL;
349}
350
351static void sync_fence_signal_pt(struct sync_pt *pt)
352{
353	LIST_HEAD(signaled_waiters);
354	struct sync_fence *fence = pt->fence;
355	struct list_head *pos;
356	struct list_head *n;
357	unsigned long flags;
358	int status;
359
360	status = sync_fence_get_status(fence);
361
362	spin_lock_irqsave(&fence->waiter_list_lock, flags);
363	/*
364	 * this should protect against two threads racing on the signaled
365	 * false -> true transition
366	 */
367	if (status && !fence->status) {
368		list_for_each_safe(pos, n, &fence->waiter_list_head)
369			list_move(pos, &signaled_waiters);
370
371		fence->status = status;
372	} else {
373		status = 0;
374	}
375	spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
376
377	if (status) {
378		list_for_each_safe(pos, n, &signaled_waiters) {
379			struct sync_fence_waiter *waiter =
380				container_of(pos, struct sync_fence_waiter,
381					     waiter_list);
382
383			waiter->callback(fence, waiter->callback_data);
384			list_del(pos);
385			kfree(waiter);
386		}
387		wake_up(&fence->wq);
388	}
389}
390
391int sync_fence_wait_async(struct sync_fence *fence,
392			  void (*callback)(struct sync_fence *, void *data),
393			  void *callback_data)
394{
395	struct sync_fence_waiter *waiter;
396	unsigned long flags;
397	int err = 0;
398
399	waiter = kzalloc(sizeof(struct sync_fence_waiter), GFP_KERNEL);
400	if (waiter == NULL)
401		return -ENOMEM;
402
403	waiter->callback = callback;
404	waiter->callback_data = callback_data;
405
406	spin_lock_irqsave(&fence->waiter_list_lock, flags);
407
408	if (fence->status) {
409		kfree(waiter);
410		err = fence->status;
411		goto out;
412	}
413
414	list_add_tail(&waiter->waiter_list, &fence->waiter_list_head);
415out:
416	spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
417
418	return err;
419}
420
421int sync_fence_wait(struct sync_fence *fence, long timeout)
422{
423	int err;
424
425	if (timeout) {
426		timeout = msecs_to_jiffies(timeout);
427		err = wait_event_interruptible_timeout(fence->wq,
428						       fence->status != 0,
429						       timeout);
430	} else {
431		err = wait_event_interruptible(fence->wq, fence->status != 0);
432	}
433
434	if (err < 0)
435		return err;
436
437	if (fence->status < 0)
438		return fence->status;
439
440	if (fence->status == 0)
441		return -ETIME;
442
443	return 0;
444}
445
446static int sync_fence_release(struct inode *inode, struct file *file)
447{
448	struct sync_fence *fence = file->private_data;
449
450	sync_fence_free_pts(fence);
451	kfree(fence);
452
453	return 0;
454}
455
456static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg)
457{
458	__s32 value;
459
460	if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
461		return -EFAULT;
462
463	return sync_fence_wait(fence, value);
464}
465
466static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg)
467{
468	int fd = get_unused_fd();
469	int err;
470	struct sync_fence *fence2, *fence3;
471	struct sync_merge_data data;
472
473	if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
474		return -EFAULT;
475
476	fence2 = sync_fence_fdget(data.fd2);
477	if (fence2 == NULL) {
478		err = -ENOENT;
479		goto err_put_fd;
480	}
481
482	data.name[sizeof(data.name) - 1] = '\0';
483	fence3 = sync_fence_merge(data.name, fence, fence2);
484	if (fence3 == NULL) {
485		err = -ENOMEM;
486		goto err_put_fence2;
487	}
488
489	data.fence = fd;
490	if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
491		err = -EFAULT;
492		goto err_put_fence3;
493	}
494
495	sync_fence_install(fence3, fd);
496	sync_fence_put(fence2);
497	return 0;
498
499err_put_fence3:
500	sync_fence_put(fence3);
501
502err_put_fence2:
503	sync_fence_put(fence2);
504
505err_put_fd:
506	put_unused_fd(fd);
507	return err;
508}
509
510
511static long sync_fence_ioctl(struct file *file, unsigned int cmd,
512			     unsigned long arg)
513{
514	struct sync_fence *fence = file->private_data;
515	switch (cmd) {
516	case SYNC_IOC_WAIT:
517		return sync_fence_ioctl_wait(fence, arg);
518
519	case SYNC_IOC_MERGE:
520		return sync_fence_ioctl_merge(fence, arg);
521	default:
522		return -ENOTTY;
523	}
524}
525
526