// SPDX-License-Identifier: GPL-2.0-only/* * Sync File validation framework * * Copyright (C) 2012 Google, Inc. */ #include <linux/file.h>#include <linux/fs.h>#include <linux/uaccess.h>#include <linux/panic.h>#include <linux/slab.h>#include <linux/sync_file.h> #include "sync_debug.h" #define CREATE_TRACE_POINTS#include "sync_trace.h" /* * SW SYNC validation framework * * A sync object driver that uses a 32bit counter to coordinate * synchronization. Useful when there is no hardware primitive backing * the synchronization. * * To start the framework just open: * * <debugfs>/sync/sw_sync * * That will create a sync timeline, all fences created under this timeline * file descriptor will belong to the this timeline. * * The 'sw_sync' file can be opened many times as to create different * timelines. * * Fences can be created with SW_SYNC_IOC_CREATE_FENCE ioctl with struct * sw_sync_create_fence_data as parameter. * * To increment the timeline counter, SW_SYNC_IOC_INC ioctl should be used * with the increment as u32. This will update the last signaled value * from the timeline and signal any fence that has a seqno smaller or equal * to it. * * struct sw_sync_create_fence_data * @value: the seqno to initialise the fence with * @name: the name of the new sync point * @fence: return the fd of the new sync_file with the created fence */struct sw_sync_create_fence_data { __u32 value; char name[32]; __s32 fence; /* fd of new fence */}; /** * struct sw_sync_get_deadline - get the deadline hint of a sw_sync fence * @deadline_ns: absolute time of the deadline * @pad: must be zero * @fence_fd: the sw_sync fence fd (in) * * Return the earliest deadline set on the fence. The timebase for the * deadline is CLOCK_MONOTONIC (same as vblank). If there is no deadline * set on the fence, this ioctl will return -ENOENT. */struct sw_sync_get_deadline { __u64 deadline_ns; __u32 pad; __s32 fence_fd;}; #define SW_SYNC_IOC_MAGIC 'W' #define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ struct sw_sync_create_fence_data) #define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)#define SW_SYNC_GET_DEADLINE _IOWR(SW_SYNC_IOC_MAGIC, 2, \ struct sw_sync_get_deadline) #define SW_SYNC_HAS_DEADLINE_BIT DMA_FENCE_FLAG_USER_BITS static const struct dma_fence_ops timeline_fence_ops; static inline struct sync_pt *dma_fence_to_sync_pt(struct dma_fence *fence){ if (fence->ops != &timeline_fence_ops) return NULL; return container_of(fence, struct sync_pt, base);} /** * sync_timeline_create() - creates a sync object * @name: sync_timeline name * * Creates a new sync_timeline. Returns the sync_timeline object or NULL in * case of error. */static struct sync_timeline *sync_timeline_create(const char *name){ struct sync_timeline *obj; obj = kzalloc_obj(*obj); if (!obj) return NULL; kref_init(&obj->kref); obj->context = dma_fence_context_alloc(1); strscpy(obj->name, name, sizeof(obj->name)); obj->pt_tree = RB_ROOT; INIT_LIST_HEAD(&obj->pt_list); spin_lock_init(&obj->lock); sync_timeline_debug_add(obj); return obj;} static void sync_timeline_free(struct kref *kref){ struct sync_timeline *obj = container_of(kref, struct sync_timeline, kref); sync_timeline_debug_remove(obj); kfree(obj);} static void sync_timeline_get(struct sync_timeline *obj){ kref_get(&obj->kref);} static void sync_timeline_put(struct sync_timeline *obj){ kref_put(&obj->kref, sync_timeline_free);} static const char *timeline_fence_get_driver_name(struct dma_fence *fence){ return "sw_sync";} static const char *timeline_fence_get_timeline_name(struct dma_fence *fence){ struct sync_timeline *parent = dma_fence_parent(fence); return parent->name;} static void timeline_fence_release(struct dma_fence *fence){ struct sync_pt *pt = dma_fence_to_sync_pt(fence); struct sync_timeline *parent = dma_fence_parent(fence); unsigned long flags; dma_fence_lock_irqsave(fence, flags); if (!list_empty(&pt->link)) { list_del(&pt->link); rb_erase(&pt->node, &parent->pt_tree); } dma_fence_unlock_irqrestore(fence, flags); sync_timeline_put(parent); dma_fence_free(fence);} static bool timeline_fence_signaled(struct dma_fence *fence){ struct sync_timeline *parent = dma_fence_parent(fence); return !__dma_fence_is_later(fence, fence->seqno, parent->value);} static void timeline_fence_set_deadline(struct dma_fence *fence, ktime_t deadline){ struct sync_pt *pt = dma_fence_to_sync_pt(fence); unsigned long flags; dma_fence_lock_irqsave(fence, flags); if (test_bit(SW_SYNC_HAS_DEADLINE_BIT, &fence->flags)) { if (ktime_before(deadline, pt->deadline)) pt->deadline = deadline; } else { pt->deadline = deadline; __set_bit(SW_SYNC_HAS_DEADLINE_BIT, &fence->flags); } dma_fence_unlock_irqrestore(fence, flags);} static const struct dma_fence_ops timeline_fence_ops = { .get_driver_name = timeline_fence_get_driver_name, .get_timeline_name = timeline_fence_get_timeline_name, .signaled = timeline_fence_signaled, .release = timeline_fence_release, .set_deadline = timeline_fence_set_deadline,}; /** * sync_timeline_signal() - signal a status change on a sync_timeline * @obj: sync_timeline to signal * @inc: num to increment on timeline->value * * A sync implementation should call this any time one of it's fences * has signaled or has an error condition. */static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc){ LIST_HEAD(signalled); struct sync_pt *pt, *next; trace_sync_timeline(obj); spin_lock_irq(&obj->lock); obj->value += inc; list_for_each_entry_safe(pt, next, &obj->pt_list, link) { if (!timeline_fence_signaled(&pt->base)) break; dma_fence_get(&pt->base); list_move_tail(&pt->link, &signalled); rb_erase(&pt->node, &obj->pt_tree); dma_fence_signal_locked(&pt->base); } spin_unlock_irq(&obj->lock); list_for_each_entry_safe(pt, next, &signalled, link) { list_del_init(&pt->link); dma_fence_put(&pt->base); }} /** * sync_pt_create() - creates a sync pt * @obj: parent sync_timeline * @value: value of the fence * * Creates a new sync_pt (fence) as a child of @parent. @size bytes will be * allocated allowing for implementation specific data to be kept after * the generic sync_timeline struct. Returns the sync_pt object or * NULL in case of error. */static struct sync_pt *sync_pt_create(struct sync_timeline *obj, unsigned int value){ struct sync_pt *pt; pt = kzalloc_obj(*pt); if (!pt) return NULL; sync_timeline_get(obj); dma_fence_init(&pt->base, &timeline_fence_ops, &obj->lock, obj->context, value); INIT_LIST_HEAD(&pt->link); spin_lock_irq(&obj->lock); if (!dma_fence_is_signaled_locked(&pt->base)) { struct rb_node **p = &obj->pt_tree.rb_node; struct rb_node *parent = NULL; while (*p) { struct sync_pt *other; int cmp; parent = *p; other = rb_entry(parent, typeof(*pt), node); cmp = value - other->base.seqno; if (cmp > 0) { p = &parent->rb_right; } else if (cmp < 0) { p = &parent->rb_left; } else { if (dma_fence_get_rcu(&other->base)) { sync_timeline_put(obj); kfree(pt); pt = other; goto unlock; } p = &parent->rb_left; } } rb_link_node(&pt->node, parent, p); rb_insert_color(&pt->node, &obj->pt_tree); parent = rb_next(&pt->node); list_add_tail(&pt->link, parent ? &rb_entry(parent, typeof(*pt), node)->link : &obj->pt_list); }unlock: spin_unlock_irq(&obj->lock); return pt;} /* * *WARNING* * * improper use of this can result in deadlocking kernel drivers from userspace. */ /* opening sw_sync create a new sync obj */static int sw_sync_debugfs_open(struct inode *inode, struct file *file){ struct sync_timeline *obj; char task_comm[TASK_COMM_LEN]; get_task_comm(task_comm, current); obj = sync_timeline_create(task_comm); if (!obj) return -ENOMEM; file->private_data = obj; return 0;} static int sw_sync_debugfs_release(struct inode *inode, struct file *file){ struct sync_timeline *obj = file->private_data; struct sync_pt *pt, *next; spin_lock_irq(&obj->lock); list_for_each_entry_safe(pt, next, &obj->pt_list, link) { dma_fence_set_error(&pt->base, -ENOENT); dma_fence_signal_locked(&pt->base); } spin_unlock_irq(&obj->lock); sync_timeline_put(obj); return 0;} static long sw_sync_ioctl_create_fence(struct sync_timeline *obj, unsigned long arg){ int fd = get_unused_fd_flags(O_CLOEXEC); int err; struct sync_pt *pt; struct sync_file *sync_file; struct sw_sync_create_fence_data data; /* SW sync fence are inherently unsafe and can deadlock the kernel */ add_taint(TAINT_SOFTLOCKUP, LOCKDEP_STILL_OK); if (fd < 0) return fd; if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { err = -EFAULT; goto err; } pt = sync_pt_create(obj, data.value); if (!pt) { err = -ENOMEM; goto err; } sync_file = sync_file_create(&pt->base); dma_fence_put(&pt->base); if (!sync_file) { err = -ENOMEM; goto err; } data.fence = fd; if (copy_to_user((void __user *)arg, &data, sizeof(data))) { fput(sync_file->file); err = -EFAULT; goto err; } fd_install(fd, sync_file->file); return 0; err: put_unused_fd(fd); return err;} static long sw_sync_ioctl_inc(struct sync_timeline *obj, unsigned long arg){ u32 value; if (copy_from_user(&value, (void __user *)arg, sizeof(value))) return -EFAULT; while (value > INT_MAX) { sync_timeline_signal(obj, INT_MAX); value -= INT_MAX; } sync_timeline_signal(obj, value); return 0;} static int sw_sync_ioctl_get_deadline(struct sync_timeline *obj, unsigned long arg){ struct sw_sync_get_deadline data; struct dma_fence *fence; unsigned long flags; struct sync_pt *pt; int ret = 0; if (copy_from_user(&data, (void __user *)arg, sizeof(data))) return -EFAULT; if (data.deadline_ns || data.pad) return -EINVAL; fence = sync_file_get_fence(data.fence_fd); if (!fence) return -EINVAL; pt = dma_fence_to_sync_pt(fence); if (!pt) { ret = -EINVAL; goto put_fence; } dma_fence_lock_irqsave(fence, flags); if (!test_bit(SW_SYNC_HAS_DEADLINE_BIT, &fence->flags)) { ret = -ENOENT; goto unlock; } data.deadline_ns = ktime_to_ns(pt->deadline); dma_fence_unlock_irqrestore(fence, flags); dma_fence_put(fence); if (ret) return ret; if (copy_to_user((void __user *)arg, &data, sizeof(data))) return -EFAULT; return 0; unlock: dma_fence_unlock_irqrestore(fence, flags);put_fence: dma_fence_put(fence); return ret;} static long sw_sync_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ struct sync_timeline *obj = file->private_data; switch (cmd) { case SW_SYNC_IOC_CREATE_FENCE: return sw_sync_ioctl_create_fence(obj, arg); case SW_SYNC_IOC_INC: return sw_sync_ioctl_inc(obj, arg); case SW_SYNC_GET_DEADLINE: return sw_sync_ioctl_get_deadline(obj, arg); default: return -ENOTTY; }} const struct file_operations sw_sync_debugfs_fops = { .open = sw_sync_debugfs_open, .release = sw_sync_debugfs_release, .unlocked_ioctl = sw_sync_ioctl, .compat_ioctl = compat_ptr_ioctl,};
// SPDX-License-Identifier: GPL-2.0-only
/*
* Sync File validation framework
*
* Copyright (C) 2012 Google, Inc.
*/
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/panic.h>
#include <linux/slab.h>
#include <linux/sync_file.h>
#include "sync_debug.h"
#define CREATE_TRACE_POINTS
#include "sync_trace.h"
/*
* SW SYNC validation framework
*
* A sync object driver that uses a 32bit counter to coordinate
* synchronization. Useful when there is no hardware primitive backing
* the synchronization.
*
* To start the framework just open:
*
* <debugfs>/sync/sw_sync
*
* That will create a sync timeline, all fences created under this timeline
* file descriptor will belong to the this timeline.
*
* The 'sw_sync' file can be opened many times as to create different
* timelines.
*
* Fences can be created with SW_SYNC_IOC_CREATE_FENCE ioctl with struct
* sw_sync_create_fence_data as parameter.
*
* To increment the timeline counter, SW_SYNC_IOC_INC ioctl should be used
* with the increment as u32. This will update the last signaled value
* from the timeline and signal any fence that has a seqno smaller or equal
* to it.
*
* struct sw_sync_create_fence_data
* @value: the seqno to initialise the fence with
* @name: the name of the new sync point
* @fence: return the fd of the new sync_file with the created fence
*/
struct sw_sync_create_fence_data {
__u32 value;
char name[32];
__s32 fence; /* fd of new fence */
};
/**
* struct sw_sync_get_deadline - get the deadline hint of a sw_sync fence
* @deadline_ns: absolute time of the deadline
* @pad: must be zero
* @fence_fd: the sw_sync fence fd (in)
*
* Return the earliest deadline set on the fence. The timebase for the
* deadline is CLOCK_MONOTONIC (same as vblank). If there is no deadline
* set on the fence, this ioctl will return -ENOENT.
*/
struct sw_sync_get_deadline {
__u64 deadline_ns;
__u32 pad;
__s32 fence_fd;
};
#define SW_SYNC_IOC_MAGIC 'W'
#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\
struct sw_sync_create_fence_data)
#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
#define SW_SYNC_GET_DEADLINE _IOWR(SW_SYNC_IOC_MAGIC, 2, \
struct sw_sync_get_deadline)
#define SW_SYNC_HAS_DEADLINE_BIT DMA_FENCE_FLAG_USER_BITS
static const struct dma_fence_ops timeline_fence_ops;
static inline struct sync_pt *dma_fence_to_sync_pt(struct dma_fence *fence)
{
if (fence->ops != &timeline_fence_ops)
return NULL;
return container_of(fence, struct sync_pt, base);
}
/**
* sync_timeline_create() - creates a sync object
* @name: sync_timeline name
*
* Creates a new sync_timeline. Returns the sync_timeline object or NULL in
* case of error.
*/
static struct sync_timeline *sync_timeline_create(const char *name)
{
struct sync_timeline *obj;
obj = kzalloc_obj(*obj);
if (!obj)
return NULL;
kref_init(&obj->kref);
obj->context = dma_fence_context_alloc(1);
strscpy(obj->name, name, sizeof(obj->name));
obj->pt_tree = RB_ROOT;
INIT_LIST_HEAD(&obj->pt_list);
spin_lock_init(&obj->lock);
sync_timeline_debug_add(obj);
return obj;
}
static void sync_timeline_free(struct kref *kref)
{
struct sync_timeline *obj =
container_of(kref, struct sync_timeline, kref);
sync_timeline_debug_remove(obj);
kfree(obj);
}
static void sync_timeline_get(struct sync_timeline *obj)
{
kref_get(&obj->kref);
}
static void sync_timeline_put(struct sync_timeline *obj)
{
kref_put(&obj->kref, sync_timeline_free);
}
static const char *timeline_fence_get_driver_name(struct dma_fence *fence)
{
return "sw_sync";
}
static const char *timeline_fence_get_timeline_name(struct dma_fence *fence)
{
struct sync_timeline *parent = dma_fence_parent(fence);
return parent->name;
}
static void timeline_fence_release(struct dma_fence *fence)
{
struct sync_pt *pt = dma_fence_to_sync_pt(fence);
struct sync_timeline *parent = dma_fence_parent(fence);
unsigned long flags;
dma_fence_lock_irqsave(fence, flags);
if (!list_empty(&pt->link)) {
list_del(&pt->link);
rb_erase(&pt->node, &parent->pt_tree);
}
dma_fence_unlock_irqrestore(fence, flags);
sync_timeline_put(parent);
dma_fence_free(fence);
}
static bool timeline_fence_signaled(struct dma_fence *fence)
{
struct sync_timeline *parent = dma_fence_parent(fence);
return !__dma_fence_is_later(fence, fence->seqno, parent->value);
}
static void timeline_fence_set_deadline(struct dma_fence *fence, ktime_t deadline)
{
struct sync_pt *pt = dma_fence_to_sync_pt(fence);
unsigned long flags;
dma_fence_lock_irqsave(fence, flags);
if (test_bit(SW_SYNC_HAS_DEADLINE_BIT, &fence->flags)) {
if (ktime_before(deadline, pt->deadline))
pt->deadline = deadline;
} else {
pt->deadline = deadline;
__set_bit(SW_SYNC_HAS_DEADLINE_BIT, &fence->flags);
}
dma_fence_unlock_irqrestore(fence, flags);
}
static const struct dma_fence_ops timeline_fence_ops = {
.get_driver_name = timeline_fence_get_driver_name,
.get_timeline_name = timeline_fence_get_timeline_name,
.signaled = timeline_fence_signaled,
.release = timeline_fence_release,
.set_deadline = timeline_fence_set_deadline,
};
/**
* sync_timeline_signal() - signal a status change on a sync_timeline
* @obj: sync_timeline to signal
* @inc: num to increment on timeline->value
*
* A sync implementation should call this any time one of it's fences
* has signaled or has an error condition.
*/
static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc)
{
LIST_HEAD(signalled);
struct sync_pt *pt, *next;
trace_sync_timeline(obj);
spin_lock_irq(&obj->lock);
obj->value += inc;
list_for_each_entry_safe(pt, next, &obj->pt_list, link) {
if (!timeline_fence_signaled(&pt->base))
break;
dma_fence_get(&pt->base);
list_move_tail(&pt->link, &signalled);
rb_erase(&pt->node, &obj->pt_tree);
dma_fence_signal_locked(&pt->base);
}
spin_unlock_irq(&obj->lock);
list_for_each_entry_safe(pt, next, &signalled, link) {
list_del_init(&pt->link);
dma_fence_put(&pt->base);
}
}
/**
* sync_pt_create() - creates a sync pt
* @obj: parent sync_timeline
* @value: value of the fence
*
* Creates a new sync_pt (fence) as a child of @parent. @size bytes will be
* allocated allowing for implementation specific data to be kept after
* the generic sync_timeline struct. Returns the sync_pt object or
* NULL in case of error.
*/
static struct sync_pt *sync_pt_create(struct sync_timeline *obj,
unsigned int value)
{
struct sync_pt *pt;
pt = kzalloc_obj(*pt);
if (!pt)
return NULL;
sync_timeline_get(obj);
dma_fence_init(&pt->base, &timeline_fence_ops, &obj->lock,
obj->context, value);
INIT_LIST_HEAD(&pt->link);
spin_lock_irq(&obj->lock);
if (!dma_fence_is_signaled_locked(&pt->base)) {
struct rb_node **p = &obj->pt_tree.rb_node;
struct rb_node *parent = NULL;
while (*p) {
struct sync_pt *other;
int cmp;
parent = *p;
other = rb_entry(parent, typeof(*pt), node);
cmp = value - other->base.seqno;
if (cmp > 0) {
p = &parent->rb_right;
} else if (cmp < 0) {
p = &parent->rb_left;
} else {
if (dma_fence_get_rcu(&other->base)) {
sync_timeline_put(obj);
kfree(pt);
pt = other;
goto unlock;
}
p = &parent->rb_left;
}
}
rb_link_node(&pt->node, parent, p);
rb_insert_color(&pt->node, &obj->pt_tree);
parent = rb_next(&pt->node);
list_add_tail(&pt->link,
parent ? &rb_entry(parent, typeof(*pt), node)->link : &obj->pt_list);
}
unlock:
spin_unlock_irq(&obj->lock);
return pt;
}
/*
* *WARNING*
*
* improper use of this can result in deadlocking kernel drivers from userspace.
*/
/* opening sw_sync create a new sync obj */
static int sw_sync_debugfs_open(struct inode *inode, struct file *file)
{
struct sync_timeline *obj;
char task_comm[TASK_COMM_LEN];
get_task_comm(task_comm, current);
obj = sync_timeline_create(task_comm);
if (!obj)
return -ENOMEM;
file->private_data = obj;
return 0;
}
static int sw_sync_debugfs_release(struct inode *inode, struct file *file)
{
struct sync_timeline *obj = file->private_data;
struct sync_pt *pt, *next;
spin_lock_irq(&obj->lock);
list_for_each_entry_safe(pt, next, &obj->pt_list, link) {
dma_fence_set_error(&pt->base, -ENOENT);
dma_fence_signal_locked(&pt->base);
}
spin_unlock_irq(&obj->lock);
sync_timeline_put(obj);
return 0;
}
static long sw_sync_ioctl_create_fence(struct sync_timeline *obj,
unsigned long arg)
{
int fd = get_unused_fd_flags(O_CLOEXEC);
int err;
struct sync_pt *pt;
struct sync_file *sync_file;
struct sw_sync_create_fence_data data;
/* SW sync fence are inherently unsafe and can deadlock the kernel */
add_taint(TAINT_SOFTLOCKUP, LOCKDEP_STILL_OK);
if (fd < 0)
return fd;
if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
err = -EFAULT;
goto err;
}
pt = sync_pt_create(obj, data.value);
if (!pt) {
err = -ENOMEM;
goto err;
}
sync_file = sync_file_create(&pt->base);
dma_fence_put(&pt->base);
if (!sync_file) {
err = -ENOMEM;
goto err;
}
data.fence = fd;
if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
fput(sync_file->file);
err = -EFAULT;
goto err;
}
fd_install(fd, sync_file->file);
return 0;
err:
put_unused_fd(fd);
return err;
}
static long sw_sync_ioctl_inc(struct sync_timeline *obj, unsigned long arg)
{
u32 value;
if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
return -EFAULT;
while (value > INT_MAX) {
sync_timeline_signal(obj, INT_MAX);
value -= INT_MAX;
}
sync_timeline_signal(obj, value);
return 0;
}
static int sw_sync_ioctl_get_deadline(struct sync_timeline *obj, unsigned long arg)
{
struct sw_sync_get_deadline data;
struct dma_fence *fence;
unsigned long flags;
struct sync_pt *pt;
int ret = 0;
if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
return -EFAULT;
if (data.deadline_ns || data.pad)
return -EINVAL;
fence = sync_file_get_fence(data.fence_fd);
if (!fence)
return -EINVAL;
pt = dma_fence_to_sync_pt(fence);
if (!pt) {
ret = -EINVAL;
goto put_fence;
}
dma_fence_lock_irqsave(fence, flags);
if (!test_bit(SW_SYNC_HAS_DEADLINE_BIT, &fence->flags)) {
ret = -ENOENT;
goto unlock;
}
data.deadline_ns = ktime_to_ns(pt->deadline);
dma_fence_unlock_irqrestore(fence, flags);
dma_fence_put(fence);
if (ret)
return ret;
if (copy_to_user((void __user *)arg, &data, sizeof(data)))
return -EFAULT;
return 0;
unlock:
dma_fence_unlock_irqrestore(fence, flags);
put_fence:
dma_fence_put(fence);
return ret;
}
static long sw_sync_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct sync_timeline *obj = file->private_data;
switch (cmd) {
case SW_SYNC_IOC_CREATE_FENCE:
return sw_sync_ioctl_create_fence(obj, arg);
case SW_SYNC_IOC_INC:
return sw_sync_ioctl_inc(obj, arg);
case SW_SYNC_GET_DEADLINE:
return sw_sync_ioctl_get_deadline(obj, arg);
default:
return -ENOTTY;
}
}
const struct file_operations sw_sync_debugfs_fops = {
.open = sw_sync_debugfs_open,
.release = sw_sync_debugfs_release,
.unlocked_ioctl = sw_sync_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};