sync.c revision cc3c5cdc7bc16b78b6c59f0720542965a67d1c81
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/debugfs.h>
18#include <linux/export.h>
19#include <linux/file.h>
20#include <linux/fs.h>
21#include <linux/kernel.h>
22#include <linux/poll.h>
23#include <linux/sched.h>
24#include <linux/seq_file.h>
25#include <linux/slab.h>
26#include <linux/uaccess.h>
27#include <linux/anon_inodes.h>
28
29#include "sync.h"
30
31static void sync_fence_signal_pt(struct sync_pt *pt);
32static int _sync_pt_has_signaled(struct sync_pt *pt);
33
34static LIST_HEAD(sync_timeline_list_head);
35static DEFINE_SPINLOCK(sync_timeline_list_lock);
36
37static LIST_HEAD(sync_fence_list_head);
38static DEFINE_SPINLOCK(sync_fence_list_lock);
39
40struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
41					   int size, const char *name)
42{
43	struct sync_timeline *obj;
44	unsigned long flags;
45
46	if (size < sizeof(struct sync_timeline))
47		return NULL;
48
49	obj = kzalloc(size, GFP_KERNEL);
50	if (obj == NULL)
51		return NULL;
52
53	obj->ops = ops;
54	strlcpy(obj->name, name, sizeof(obj->name));
55
56	INIT_LIST_HEAD(&obj->child_list_head);
57	spin_lock_init(&obj->child_list_lock);
58
59	INIT_LIST_HEAD(&obj->active_list_head);
60	spin_lock_init(&obj->active_list_lock);
61
62	spin_lock_irqsave(&sync_timeline_list_lock, flags);
63	list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
64	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
65
66	return obj;
67}
68EXPORT_SYMBOL(sync_timeline_create);
69
70static void sync_timeline_free(struct sync_timeline *obj)
71{
72	unsigned long flags;
73
74	if (obj->ops->release_obj)
75		obj->ops->release_obj(obj);
76
77	spin_lock_irqsave(&sync_timeline_list_lock, flags);
78	list_del(&obj->sync_timeline_list);
79	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
80
81	kfree(obj);
82}
83
84void sync_timeline_destroy(struct sync_timeline *obj)
85{
86	unsigned long flags;
87	bool needs_freeing;
88
89	spin_lock_irqsave(&obj->child_list_lock, flags);
90	obj->destroyed = true;
91	needs_freeing = list_empty(&obj->child_list_head);
92	spin_unlock_irqrestore(&obj->child_list_lock, flags);
93
94	if (needs_freeing)
95		sync_timeline_free(obj);
96	else
97		sync_timeline_signal(obj);
98}
99EXPORT_SYMBOL(sync_timeline_destroy);
100
101static void sync_timeline_add_pt(struct sync_timeline *obj, struct sync_pt *pt)
102{
103	unsigned long flags;
104
105	pt->parent = obj;
106
107	spin_lock_irqsave(&obj->child_list_lock, flags);
108	list_add_tail(&pt->child_list, &obj->child_list_head);
109	spin_unlock_irqrestore(&obj->child_list_lock, flags);
110}
111
112static void sync_timeline_remove_pt(struct sync_pt *pt)
113{
114	struct sync_timeline *obj = pt->parent;
115	unsigned long flags;
116	bool needs_freeing;
117
118	spin_lock_irqsave(&obj->active_list_lock, flags);
119	if (!list_empty(&pt->active_list))
120		list_del_init(&pt->active_list);
121	spin_unlock_irqrestore(&obj->active_list_lock, flags);
122
123	spin_lock_irqsave(&obj->child_list_lock, flags);
124	list_del(&pt->child_list);
125	needs_freeing = obj->destroyed && list_empty(&obj->child_list_head);
126	spin_unlock_irqrestore(&obj->child_list_lock, flags);
127
128	if (needs_freeing)
129		sync_timeline_free(obj);
130}
131
132void sync_timeline_signal(struct sync_timeline *obj)
133{
134	unsigned long flags;
135	LIST_HEAD(signaled_pts);
136	struct list_head *pos, *n;
137
138	spin_lock_irqsave(&obj->active_list_lock, flags);
139
140	list_for_each_safe(pos, n, &obj->active_list_head) {
141		struct sync_pt *pt =
142			container_of(pos, struct sync_pt, active_list);
143
144		if (_sync_pt_has_signaled(pt))
145			list_move(pos, &signaled_pts);
146	}
147
148	spin_unlock_irqrestore(&obj->active_list_lock, flags);
149
150	list_for_each_safe(pos, n, &signaled_pts) {
151		struct sync_pt *pt =
152			container_of(pos, struct sync_pt, active_list);
153
154		list_del_init(pos);
155		sync_fence_signal_pt(pt);
156	}
157}
158EXPORT_SYMBOL(sync_timeline_signal);
159
160struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size)
161{
162	struct sync_pt *pt;
163
164	if (size < sizeof(struct sync_pt))
165		return NULL;
166
167	pt = kzalloc(size, GFP_KERNEL);
168	if (pt == NULL)
169		return NULL;
170
171	INIT_LIST_HEAD(&pt->active_list);
172	sync_timeline_add_pt(parent, pt);
173
174	return pt;
175}
176EXPORT_SYMBOL(sync_pt_create);
177
178void sync_pt_free(struct sync_pt *pt)
179{
180	if (pt->parent->ops->free_pt)
181		pt->parent->ops->free_pt(pt);
182
183	sync_timeline_remove_pt(pt);
184
185	kfree(pt);
186}
187EXPORT_SYMBOL(sync_pt_free);
188
189/* call with pt->parent->active_list_lock held */
190static int _sync_pt_has_signaled(struct sync_pt *pt)
191{
192	int old_status = pt->status;
193
194	if (!pt->status)
195		pt->status = pt->parent->ops->has_signaled(pt);
196
197	if (!pt->status && pt->parent->destroyed)
198		pt->status = -ENOENT;
199
200	if (pt->status != old_status)
201		pt->timestamp = ktime_get();
202
203	return pt->status;
204}
205
206static struct sync_pt *sync_pt_dup(struct sync_pt *pt)
207{
208	return pt->parent->ops->dup(pt);
209}
210
211/* Adds a sync pt to the active queue.  Called when added to a fence */
212static void sync_pt_activate(struct sync_pt *pt)
213{
214	struct sync_timeline *obj = pt->parent;
215	unsigned long flags;
216	int err;
217
218	spin_lock_irqsave(&obj->active_list_lock, flags);
219
220	err = _sync_pt_has_signaled(pt);
221	if (err != 0)
222		goto out;
223
224	list_add_tail(&pt->active_list, &obj->active_list_head);
225
226out:
227	spin_unlock_irqrestore(&obj->active_list_lock, flags);
228}
229
230static int sync_fence_release(struct inode *inode, struct file *file);
231static unsigned int sync_fence_poll(struct file *file, poll_table *wait);
232static long sync_fence_ioctl(struct file *file, unsigned int cmd,
233			     unsigned long arg);
234
235
236static const struct file_operations sync_fence_fops = {
237	.release = sync_fence_release,
238	.poll = sync_fence_poll,
239	.unlocked_ioctl = sync_fence_ioctl,
240};
241
242static struct sync_fence *sync_fence_alloc(const char *name)
243{
244	struct sync_fence *fence;
245	unsigned long flags;
246
247	fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL);
248	if (fence == NULL)
249		return NULL;
250
251	fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops,
252					 fence, 0);
253	if (fence->file == NULL)
254		goto err;
255
256	strlcpy(fence->name, name, sizeof(fence->name));
257
258	INIT_LIST_HEAD(&fence->pt_list_head);
259	INIT_LIST_HEAD(&fence->waiter_list_head);
260	spin_lock_init(&fence->waiter_list_lock);
261
262	init_waitqueue_head(&fence->wq);
263
264	spin_lock_irqsave(&sync_fence_list_lock, flags);
265	list_add_tail(&fence->sync_fence_list, &sync_fence_list_head);
266	spin_unlock_irqrestore(&sync_fence_list_lock, flags);
267
268	return fence;
269
270err:
271	kfree(fence);
272	return NULL;
273}
274
275/* TODO: implement a create which takes more that one sync_pt */
276struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)
277{
278	struct sync_fence *fence;
279
280	if (pt->fence)
281		return NULL;
282
283	fence = sync_fence_alloc(name);
284	if (fence == NULL)
285		return NULL;
286
287	pt->fence = fence;
288	list_add(&pt->pt_list, &fence->pt_list_head);
289	sync_pt_activate(pt);
290
291	return fence;
292}
293EXPORT_SYMBOL(sync_fence_create);
294
295static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src)
296{
297	struct list_head *pos;
298
299	list_for_each(pos, &src->pt_list_head) {
300		struct sync_pt *orig_pt =
301			container_of(pos, struct sync_pt, pt_list);
302		struct sync_pt *new_pt = sync_pt_dup(orig_pt);
303
304		if (new_pt == NULL)
305			return -ENOMEM;
306
307		new_pt->fence = dst;
308		list_add(&new_pt->pt_list, &dst->pt_list_head);
309		sync_pt_activate(new_pt);
310	}
311
312	return 0;
313}
314
315static void sync_fence_free_pts(struct sync_fence *fence)
316{
317	struct list_head *pos, *n;
318
319	list_for_each_safe(pos, n, &fence->pt_list_head) {
320		struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
321		sync_pt_free(pt);
322	}
323}
324
325struct sync_fence *sync_fence_fdget(int fd)
326{
327	struct file *file = fget(fd);
328
329	if (file == NULL)
330		return NULL;
331
332	if (file->f_op != &sync_fence_fops)
333		goto err;
334
335	return file->private_data;
336
337err:
338	fput(file);
339	return NULL;
340}
341EXPORT_SYMBOL(sync_fence_fdget);
342
343void sync_fence_put(struct sync_fence *fence)
344{
345	fput(fence->file);
346}
347EXPORT_SYMBOL(sync_fence_put);
348
349void sync_fence_install(struct sync_fence *fence, int fd)
350{
351	fd_install(fd, fence->file);
352}
353EXPORT_SYMBOL(sync_fence_install);
354
355static int sync_fence_get_status(struct sync_fence *fence)
356{
357	struct list_head *pos;
358	int status = 1;
359
360	list_for_each(pos, &fence->pt_list_head) {
361		struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
362		int pt_status = pt->status;
363
364		if (pt_status < 0) {
365			status = pt_status;
366			break;
367		} else if (status == 1) {
368			status = pt_status;
369		}
370	}
371
372	return status;
373}
374
375struct sync_fence *sync_fence_merge(const char *name,
376				    struct sync_fence *a, struct sync_fence *b)
377{
378	struct sync_fence *fence;
379	int err;
380
381	fence = sync_fence_alloc(name);
382	if (fence == NULL)
383		return NULL;
384
385	err = sync_fence_copy_pts(fence, a);
386	if (err < 0)
387		goto err;
388
389	err = sync_fence_copy_pts(fence, b);
390	if (err < 0)
391		goto err;
392
393	fence->status = sync_fence_get_status(fence);
394
395	return fence;
396err:
397	sync_fence_free_pts(fence);
398	kfree(fence);
399	return NULL;
400}
401EXPORT_SYMBOL(sync_fence_merge);
402
403static void sync_fence_signal_pt(struct sync_pt *pt)
404{
405	LIST_HEAD(signaled_waiters);
406	struct sync_fence *fence = pt->fence;
407	struct list_head *pos;
408	struct list_head *n;
409	unsigned long flags;
410	int status;
411
412	status = sync_fence_get_status(fence);
413
414	spin_lock_irqsave(&fence->waiter_list_lock, flags);
415	/*
416	 * this should protect against two threads racing on the signaled
417	 * false -> true transition
418	 */
419	if (status && !fence->status) {
420		list_for_each_safe(pos, n, &fence->waiter_list_head)
421			list_move(pos, &signaled_waiters);
422
423		fence->status = status;
424	} else {
425		status = 0;
426	}
427	spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
428
429	if (status) {
430		list_for_each_safe(pos, n, &signaled_waiters) {
431			struct sync_fence_waiter *waiter =
432				container_of(pos, struct sync_fence_waiter,
433					     waiter_list);
434
435			list_del(pos);
436			waiter->callback(fence, waiter);
437		}
438		wake_up(&fence->wq);
439	}
440}
441
442int sync_fence_wait_async(struct sync_fence *fence,
443			  struct sync_fence_waiter *waiter)
444{
445	unsigned long flags;
446	int err = 0;
447
448	spin_lock_irqsave(&fence->waiter_list_lock, flags);
449
450	if (fence->status) {
451		err = fence->status;
452		goto out;
453	}
454
455	list_add_tail(&waiter->waiter_list, &fence->waiter_list_head);
456out:
457	spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
458
459	return err;
460}
461EXPORT_SYMBOL(sync_fence_wait_async);
462
463int sync_fence_cancel_async(struct sync_fence *fence,
464			     struct sync_fence_waiter *waiter)
465{
466	struct list_head *pos;
467	struct list_head *n;
468	unsigned long flags;
469	int ret = -ENOENT;
470
471	spin_lock_irqsave(&fence->waiter_list_lock, flags);
472	/*
473	 * Make sure waiter is still in waiter_list because it is possible for
474	 * the waiter to be removed from the list while the callback is still
475	 * pending.
476	 */
477	list_for_each_safe(pos, n, &fence->waiter_list_head) {
478		struct sync_fence_waiter *list_waiter =
479			container_of(pos, struct sync_fence_waiter,
480				     waiter_list);
481		if (list_waiter == waiter) {
482			list_del(pos);
483			ret = 0;
484			break;
485		}
486	}
487	spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
488	return ret;
489}
490EXPORT_SYMBOL(sync_fence_cancel_async);
491
492int sync_fence_wait(struct sync_fence *fence, long timeout)
493{
494	int err;
495
496	if (timeout) {
497		timeout = msecs_to_jiffies(timeout);
498		err = wait_event_interruptible_timeout(fence->wq,
499						       fence->status != 0,
500						       timeout);
501	} else {
502		err = wait_event_interruptible(fence->wq, fence->status != 0);
503	}
504
505	if (err < 0)
506		return err;
507
508	if (fence->status < 0)
509		return fence->status;
510
511	if (fence->status == 0)
512		return -ETIME;
513
514	return 0;
515}
516EXPORT_SYMBOL(sync_fence_wait);
517
518static int sync_fence_release(struct inode *inode, struct file *file)
519{
520	struct sync_fence *fence = file->private_data;
521	unsigned long flags;
522
523	spin_lock_irqsave(&sync_fence_list_lock, flags);
524	list_del(&fence->sync_fence_list);
525	spin_unlock_irqrestore(&sync_fence_list_lock, flags);
526
527	sync_fence_free_pts(fence);
528
529	kfree(fence);
530
531	return 0;
532}
533
534static unsigned int sync_fence_poll(struct file *file, poll_table *wait)
535{
536	struct sync_fence *fence = file->private_data;
537
538	poll_wait(file, &fence->wq, wait);
539
540	if (fence->status == 1)
541		return POLLIN;
542	else if (fence->status < 0)
543		return POLLERR;
544	else
545		return 0;
546}
547
548static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg)
549{
550	__s32 value;
551
552	if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
553		return -EFAULT;
554
555	return sync_fence_wait(fence, value);
556}
557
558static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg)
559{
560	int fd = get_unused_fd();
561	int err;
562	struct sync_fence *fence2, *fence3;
563	struct sync_merge_data data;
564
565	if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
566		return -EFAULT;
567
568	fence2 = sync_fence_fdget(data.fd2);
569	if (fence2 == NULL) {
570		err = -ENOENT;
571		goto err_put_fd;
572	}
573
574	data.name[sizeof(data.name) - 1] = '\0';
575	fence3 = sync_fence_merge(data.name, fence, fence2);
576	if (fence3 == NULL) {
577		err = -ENOMEM;
578		goto err_put_fence2;
579	}
580
581	data.fence = fd;
582	if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
583		err = -EFAULT;
584		goto err_put_fence3;
585	}
586
587	sync_fence_install(fence3, fd);
588	sync_fence_put(fence2);
589	return 0;
590
591err_put_fence3:
592	sync_fence_put(fence3);
593
594err_put_fence2:
595	sync_fence_put(fence2);
596
597err_put_fd:
598	put_unused_fd(fd);
599	return err;
600}
601
602static int sync_fill_pt_info(struct sync_pt *pt, void *data, int size)
603{
604	struct sync_pt_info *info = data;
605	int ret;
606
607	if (size < sizeof(struct sync_pt_info))
608		return -ENOMEM;
609
610	info->len = sizeof(struct sync_pt_info);
611
612	if (pt->parent->ops->fill_driver_data) {
613		ret = pt->parent->ops->fill_driver_data(pt, info->driver_data,
614							size - sizeof(*info));
615		if (ret < 0)
616			return ret;
617
618		info->len += ret;
619	}
620
621	strlcpy(info->obj_name, pt->parent->name, sizeof(info->obj_name));
622	strlcpy(info->driver_name, pt->parent->ops->driver_name,
623		sizeof(info->driver_name));
624	info->status = pt->status;
625	info->timestamp_ns = ktime_to_ns(pt->timestamp);
626
627	return info->len;
628}
629
630static long sync_fence_ioctl_fence_info(struct sync_fence *fence,
631					unsigned long arg)
632{
633	struct sync_fence_info_data *data;
634	struct list_head *pos;
635	__u32 size;
636	__u32 len = 0;
637	int ret;
638
639	if (copy_from_user(&size, (void __user *)arg, sizeof(size)))
640		return -EFAULT;
641
642	if (size < sizeof(struct sync_fence_info_data))
643		return -EINVAL;
644
645	if (size > 4096)
646		size = 4096;
647
648	data = kzalloc(size, GFP_KERNEL);
649	if (data == NULL)
650		return -ENOMEM;
651
652	strlcpy(data->name, fence->name, sizeof(data->name));
653	data->status = fence->status;
654	len = sizeof(struct sync_fence_info_data);
655
656	list_for_each(pos, &fence->pt_list_head) {
657		struct sync_pt *pt =
658			container_of(pos, struct sync_pt, pt_list);
659
660		ret = sync_fill_pt_info(pt, (u8 *)data + len, size - len);
661
662		if (ret < 0)
663			goto out;
664
665		len += ret;
666	}
667
668	data->len = len;
669
670	if (copy_to_user((void __user *)arg, data, len))
671		ret = -EFAULT;
672	else
673		ret = 0;
674
675out:
676	kfree(data);
677
678	return ret;
679}
680
681static long sync_fence_ioctl(struct file *file, unsigned int cmd,
682			     unsigned long arg)
683{
684	struct sync_fence *fence = file->private_data;
685	switch (cmd) {
686	case SYNC_IOC_WAIT:
687		return sync_fence_ioctl_wait(fence, arg);
688
689	case SYNC_IOC_MERGE:
690		return sync_fence_ioctl_merge(fence, arg);
691
692	case SYNC_IOC_FENCE_INFO:
693		return sync_fence_ioctl_fence_info(fence, arg);
694
695	default:
696		return -ENOTTY;
697	}
698}
699
700#ifdef CONFIG_DEBUG_FS
701static const char *sync_status_str(int status)
702{
703	if (status > 0)
704		return "signaled";
705	else if (status == 0)
706		return "active";
707	else
708		return "error";
709}
710
711static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence)
712{
713	int status = pt->status;
714	seq_printf(s, "  %s%spt %s",
715		   fence ? pt->parent->name : "",
716		   fence ? "_" : "",
717		   sync_status_str(status));
718	if (pt->status) {
719		struct timeval tv = ktime_to_timeval(pt->timestamp);
720		seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec);
721	}
722
723	if (pt->parent->ops->print_pt) {
724		seq_printf(s, ": ");
725		pt->parent->ops->print_pt(s, pt);
726	}
727
728	seq_printf(s, "\n");
729}
730
731static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
732{
733	struct list_head *pos;
734	unsigned long flags;
735
736	seq_printf(s, "%s %s", obj->name, obj->ops->driver_name);
737
738	if (obj->ops->print_obj) {
739		seq_printf(s, ": ");
740		obj->ops->print_obj(s, obj);
741	}
742
743	seq_printf(s, "\n");
744
745	spin_lock_irqsave(&obj->child_list_lock, flags);
746	list_for_each(pos, &obj->child_list_head) {
747		struct sync_pt *pt =
748			container_of(pos, struct sync_pt, child_list);
749		sync_print_pt(s, pt, false);
750	}
751	spin_unlock_irqrestore(&obj->child_list_lock, flags);
752}
753
754static void sync_print_fence(struct seq_file *s, struct sync_fence *fence)
755{
756	struct list_head *pos;
757	unsigned long flags;
758
759	seq_printf(s, "%s: %s\n", fence->name, sync_status_str(fence->status));
760
761	list_for_each(pos, &fence->pt_list_head) {
762		struct sync_pt *pt =
763			container_of(pos, struct sync_pt, pt_list);
764		sync_print_pt(s, pt, true);
765	}
766
767	spin_lock_irqsave(&fence->waiter_list_lock, flags);
768	list_for_each(pos, &fence->waiter_list_head) {
769		struct sync_fence_waiter *waiter =
770			container_of(pos, struct sync_fence_waiter,
771				     waiter_list);
772
773		seq_printf(s, "waiter %pF\n", waiter->callback);
774	}
775	spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
776}
777
778static int sync_debugfs_show(struct seq_file *s, void *unused)
779{
780	unsigned long flags;
781	struct list_head *pos;
782
783	seq_printf(s, "objs:\n--------------\n");
784
785	spin_lock_irqsave(&sync_timeline_list_lock, flags);
786	list_for_each(pos, &sync_timeline_list_head) {
787		struct sync_timeline *obj =
788			container_of(pos, struct sync_timeline,
789				     sync_timeline_list);
790
791		sync_print_obj(s, obj);
792		seq_printf(s, "\n");
793	}
794	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
795
796	seq_printf(s, "fences:\n--------------\n");
797
798	spin_lock_irqsave(&sync_fence_list_lock, flags);
799	list_for_each(pos, &sync_fence_list_head) {
800		struct sync_fence *fence =
801			container_of(pos, struct sync_fence, sync_fence_list);
802
803		sync_print_fence(s, fence);
804		seq_printf(s, "\n");
805	}
806	spin_unlock_irqrestore(&sync_fence_list_lock, flags);
807	return 0;
808}
809
810static int sync_debugfs_open(struct inode *inode, struct file *file)
811{
812	return single_open(file, sync_debugfs_show, inode->i_private);
813}
814
815static const struct file_operations sync_debugfs_fops = {
816	.open           = sync_debugfs_open,
817	.read           = seq_read,
818	.llseek         = seq_lseek,
819	.release        = single_release,
820};
821
822static __init int sync_debugfs_init(void)
823{
824	debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops);
825	return 0;
826}
827
828late_initcall(sync_debugfs_init);
829
830#endif
831