#include <linux/atomic.h>
 #include <linux/device.h>
 #include <linux/srcu.h>
+#include <asm/poll.h>
 
 #include "internal.h"
 
+struct poll_table_struct;
+
 static ssize_t default_read_file(struct file *file, char __user *buf,
                                 size_t count, loff_t *ppos)
 {
  * debugfs_use_file_start() must be followed by a matching call
  * to debugfs_use_file_finish().
  */
-static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
+int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
        __acquires(&debugfs_srcu)
 {
        *srcu_idx = srcu_read_lock(&debugfs_srcu);
                return -EIO;
        return 0;
 }
+EXPORT_SYMBOL_GPL(debugfs_use_file_start);
 
 /**
  * debugfs_use_file_finish - mark the end of file data access
  * debugfs_remove_recursive() blocked by a former call to
  * debugfs_use_file_start() to proceed and return to its caller.
  */
-static void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
+void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
 {
        srcu_read_unlock(&debugfs_srcu, srcu_idx);
 }
+EXPORT_SYMBOL_GPL(debugfs_use_file_finish);
 
 #define F_DENTRY(filp) ((filp)->f_path.dentry)
 
        .open = open_proxy_open,
 };
 
+#define PROTO(args...) args
+#define ARGS(args...) args
+
+#define FULL_PROXY_FUNC(name, ret_type, filp, proto, args)             \
+static ret_type full_proxy_ ## name(proto)                             \
+{                                                                      \
+       const struct dentry *dentry = F_DENTRY(filp);                   \
+       const struct file_operations *real_fops =                       \
+               REAL_FOPS_DEREF(dentry);                                \
+       int srcu_idx;                                                   \
+       ret_type r;                                                     \
+                                                                       \
+       r = debugfs_use_file_start(dentry, &srcu_idx);                  \
+       if (likely(!r))                                         \
+               r = real_fops->name(args);                              \
+       debugfs_use_file_finish(srcu_idx);                              \
+       return r;                                                       \
+}
+
+FULL_PROXY_FUNC(llseek, loff_t, filp,
+               PROTO(struct file *filp, loff_t offset, int whence),
+               ARGS(filp, offset, whence));
+
+FULL_PROXY_FUNC(read, ssize_t, filp,
+               PROTO(struct file *filp, char __user *buf, size_t size,
+                       loff_t *ppos),
+               ARGS(filp, buf, size, ppos));
+
+FULL_PROXY_FUNC(write, ssize_t, filp,
+               PROTO(struct file *filp, const char __user *buf, size_t size,
+                       loff_t *ppos),
+               ARGS(filp, buf, size, ppos));
+
+FULL_PROXY_FUNC(unlocked_ioctl, long, filp,
+               PROTO(struct file *filp, unsigned int cmd, unsigned long arg),
+               ARGS(filp, cmd, arg));
+
+static unsigned int full_proxy_poll(struct file *filp,
+                               struct poll_table_struct *wait)
+{
+       const struct dentry *dentry = F_DENTRY(filp);
+       const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry);
+       int srcu_idx;
+       unsigned int r = 0;
+
+       if (debugfs_use_file_start(dentry, &srcu_idx)) {
+               debugfs_use_file_finish(srcu_idx);
+               return POLLHUP;
+       }
+
+       r = real_fops->poll(filp, wait);
+       debugfs_use_file_finish(srcu_idx);
+       return r;
+}
+
+static int full_proxy_release(struct inode *inode, struct file *filp)
+{
+       const struct dentry *dentry = F_DENTRY(filp);
+       const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry);
+       const struct file_operations *proxy_fops = filp->f_op;
+       int r = 0;
+
+       /*
+        * We must not protect this against removal races here: the
+        * original releaser should be called unconditionally in order
+        * not to leak any resources. Releasers must not assume that
+        * ->i_private is still being meaningful here.
+        */
+       if (real_fops->release)
+               r = real_fops->release(inode, filp);
+
+       replace_fops(filp, d_inode(dentry)->i_fop);
+       kfree((void *)proxy_fops);
+       fops_put(real_fops);
+       return 0;
+}
+
+static void __full_proxy_fops_init(struct file_operations *proxy_fops,
+                               const struct file_operations *real_fops)
+{
+       proxy_fops->release = full_proxy_release;
+       if (real_fops->llseek)
+               proxy_fops->llseek = full_proxy_llseek;
+       if (real_fops->read)
+               proxy_fops->read = full_proxy_read;
+       if (real_fops->write)
+               proxy_fops->write = full_proxy_write;
+       if (real_fops->poll)
+               proxy_fops->poll = full_proxy_poll;
+       if (real_fops->unlocked_ioctl)
+               proxy_fops->unlocked_ioctl = full_proxy_unlocked_ioctl;
+}
+
+static int full_proxy_open(struct inode *inode, struct file *filp)
+{
+       const struct dentry *dentry = F_DENTRY(filp);
+       const struct file_operations *real_fops = NULL;
+       struct file_operations *proxy_fops = NULL;
+       int srcu_idx, r;
+
+       r = debugfs_use_file_start(dentry, &srcu_idx);
+       if (r) {
+               r = -ENOENT;
+               goto out;
+       }
+
+       real_fops = REAL_FOPS_DEREF(dentry);
+       real_fops = fops_get(real_fops);
+       if (!real_fops) {
+               /* Huh? Module did not cleanup after itself at exit? */
+               WARN(1, "debugfs file owner did not clean up at exit: %pd",
+                       dentry);
+               r = -ENXIO;
+               goto out;
+       }
+
+       proxy_fops = kzalloc(sizeof(*proxy_fops), GFP_KERNEL);
+       if (!proxy_fops) {
+               r = -ENOMEM;
+               goto free_proxy;
+       }
+       __full_proxy_fops_init(proxy_fops, real_fops);
+       replace_fops(filp, proxy_fops);
+
+       if (real_fops->open) {
+               r = real_fops->open(inode, filp);
+
+               if (filp->f_op != proxy_fops) {
+                       /* No protection against file removal anymore. */
+                       WARN(1, "debugfs file owner replaced proxy fops: %pd",
+                               dentry);
+                       goto free_proxy;
+               }
+       }
+
+       goto out;
+free_proxy:
+       kfree(proxy_fops);
+       fops_put(real_fops);
+out:
+       debugfs_use_file_finish(srcu_idx);
+       return r;
+}
+
+const struct file_operations debugfs_full_proxy_file_operations = {
+       .open = full_proxy_open,
+};
+
 static struct dentry *debugfs_create_mode(const char *name, umode_t mode,
                                          struct dentry *parent, void *value,
                                          const struct file_operations *fops,
 
        return dentry;
 }
 
+static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
+                               struct dentry *parent, void *data,
+                               const struct file_operations *proxy_fops,
+                               const struct file_operations *real_fops)
+{
+       struct dentry *dentry;
+       struct inode *inode;
+
+       if (!(mode & S_IFMT))
+               mode |= S_IFREG;
+       BUG_ON(!S_ISREG(mode));
+       dentry = start_creating(name, parent);
+
+       if (IS_ERR(dentry))
+               return NULL;
+
+       inode = debugfs_get_inode(dentry->d_sb);
+       if (unlikely(!inode))
+               return failed_creating(dentry);
+
+       inode->i_mode = mode;
+       inode->i_private = data;
+
+       inode->i_fop = proxy_fops;
+       dentry->d_fsdata = (void *)real_fops;
+
+       d_instantiate(dentry, inode);
+       fsnotify_create(d_inode(dentry->d_parent), dentry);
+       return end_creating(dentry);
+}
+
 /**
  * debugfs_create_file - create a file in the debugfs filesystem
  * @name: a pointer to a string containing the name of the file to create.
                                   struct dentry *parent, void *data,
                                   const struct file_operations *fops)
 {
-       struct dentry *dentry;
-       struct inode *inode;
-
-       if (!(mode & S_IFMT))
-               mode |= S_IFREG;
-       BUG_ON(!S_ISREG(mode));
-       dentry = start_creating(name, parent);
-
-       if (IS_ERR(dentry))
-               return NULL;
-
-       inode = debugfs_get_inode(dentry->d_sb);
-       if (unlikely(!inode))
-               return failed_creating(dentry);
 
-       inode->i_mode = mode;
-       inode->i_private = data;
+       return __debugfs_create_file(name, mode, parent, data,
+                               fops ? &debugfs_full_proxy_file_operations :
+                                       &debugfs_noop_file_operations,
+                               fops);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_file);
 
-       inode->i_fop = fops ? &debugfs_open_proxy_file_operations
-               : &debugfs_noop_file_operations;
-       dentry->d_fsdata = (void *)fops;
+struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
+                                  struct dentry *parent, void *data,
+                                  const struct file_operations *fops)
+{
 
-       d_instantiate(dentry, inode);
-       fsnotify_create(d_inode(dentry->d_parent), dentry);
-       return end_creating(dentry);
+       return __debugfs_create_file(name, mode, parent, data,
+                               fops ? &debugfs_open_proxy_file_operations :
+                                       &debugfs_noop_file_operations,
+                               fops);
 }
-EXPORT_SYMBOL_GPL(debugfs_create_file);
 
 /**
  * debugfs_create_file_size - create a file in the debugfs filesystem
        inode_unlock(d_inode(parent));
        if (!ret)
                simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+
        synchronize_srcu(&debugfs_srcu);
 }
 EXPORT_SYMBOL_GPL(debugfs_remove);
        if (!__debugfs_remove(child, parent))
                simple_release_fs(&debugfs_mount, &debugfs_mount_count);
        inode_unlock(d_inode(parent));
+
        synchronize_srcu(&debugfs_srcu);
 }
 EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
 
 #include <linux/seq_file.h>
 
 #include <linux/types.h>
+#include <linux/compiler.h>
 
 struct device;
 struct file_operations;
+struct srcu_struct;
 
 struct debugfs_blob_wrapper {
        void *data;
 
 extern struct dentry *arch_debugfs_dir;
 
+extern struct srcu_struct debugfs_srcu;
+
 #if defined(CONFIG_DEBUG_FS)
 
 struct dentry *debugfs_create_file(const char *name, umode_t mode,
 void debugfs_remove(struct dentry *dentry);
 void debugfs_remove_recursive(struct dentry *dentry);
 
+int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
+       __acquires(&debugfs_srcu);
+
+void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu);
+
 struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
                 struct dentry *new_dir, const char *new_name);
 
 static inline void debugfs_remove_recursive(struct dentry *dentry)
 { }
 
+static inline int debugfs_use_file_start(const struct dentry *dentry,
+                                       int *srcu_idx)
+       __acquires(&debugfs_srcu)
+{
+       return 0;
+}
+
+static inline void debugfs_use_file_finish(int srcu_idx)
+       __releases(&debugfs_srcu)
+{ }
+
 static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
                 struct dentry *new_dir, char *new_name)
 {