Merge tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / fs / debugfs / inode.c
index 8580831..4bc1f68 100644 (file)
 #include <linux/parser.h>
 #include <linux/magic.h>
 #include <linux/slab.h>
+#include <linux/srcu.h>
+
+#include "internal.h"
 
 #define DEBUGFS_DEFAULT_MODE   0700
 
+DEFINE_SRCU(debugfs_srcu);
+
 static struct vfsmount *debugfs_mount;
 static int debugfs_mount_count;
 static bool debugfs_registered;
@@ -39,7 +44,8 @@ static struct inode *debugfs_get_inode(struct super_block *sb)
        struct inode *inode = new_inode(sb);
        if (inode) {
                inode->i_ino = get_next_ino();
-               inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+               inode->i_atime = inode->i_mtime =
+                       inode->i_ctime = current_fs_time(sb);
        }
        return inode;
 }
@@ -294,6 +300,37 @@ static struct dentry *end_creating(struct dentry *dentry)
        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.
@@ -324,29 +361,52 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode,
                                   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;
+       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 = debugfs_get_inode(dentry->d_sb);
-       if (unlikely(!inode))
-               return failed_creating(dentry);
+/**
+ * debugfs_create_file_unsafe - create a file in the debugfs filesystem
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have.
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this parameter is NULL, then the
+ *          file will be created in the root of the debugfs filesystem.
+ * @data: a pointer to something that the caller will want to get to later
+ *        on.  The inode.i_private pointer will point to this value on
+ *        the open() call.
+ * @fops: a pointer to a struct file_operations that should be used for
+ *        this file.
+ *
+ * debugfs_create_file_unsafe() is completely analogous to
+ * debugfs_create_file(), the only difference being that the fops
+ * handed it will not get protected against file removals by the
+ * debugfs core.
+ *
+ * It is your responsibility to protect your struct file_operation
+ * methods against file removals by means of debugfs_use_file_start()
+ * and debugfs_use_file_finish(). ->open() is still protected by
+ * debugfs though.
+ *
+ * Any struct file_operations defined by means of
+ * DEFINE_DEBUGFS_ATTRIBUTE() is protected against file removals and
+ * thus, may be used here.
+ */
+struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
+                                  struct dentry *parent, void *data,
+                                  const struct file_operations *fops)
+{
 
-       inode->i_mode = mode;
-       inode->i_fop = fops ? fops : &debugfs_file_operations;
-       inode->i_private = data;
-       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);
+EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe);
 
 /**
  * debugfs_create_file_size - create a file in the debugfs filesystem
@@ -461,7 +521,11 @@ struct dentry *debugfs_create_automount(const char *name,
        inode->i_flags |= S_AUTOMOUNT;
        inode->i_private = data;
        dentry->d_fsdata = (void *)f;
+       /* directory inodes start off with i_nlink == 2 (for "." entry) */
+       inc_nlink(inode);
        d_instantiate(dentry, inode);
+       inc_nlink(d_inode(dentry->d_parent));
+       fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
        return end_creating(dentry);
 }
 EXPORT_SYMBOL(debugfs_create_automount);
@@ -565,6 +629,8 @@ void debugfs_remove(struct dentry *dentry)
        inode_unlock(d_inode(parent));
        if (!ret)
                simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+
+       synchronize_srcu(&debugfs_srcu);
 }
 EXPORT_SYMBOL_GPL(debugfs_remove);
 
@@ -642,6 +708,8 @@ void debugfs_remove_recursive(struct dentry *dentry)
        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);