tracing: Have mkdir and rmdir be part of tracefs
[pandora-kernel.git] / fs / tracefs / inode.c
index 0b9cf5c..d92bdf3 100644 (file)
@@ -50,6 +50,84 @@ static const struct file_operations tracefs_file_operations = {
        .llseek =       noop_llseek,
 };
 
+static struct tracefs_dir_ops {
+       int (*mkdir)(const char *name);
+       int (*rmdir)(const char *name);
+} tracefs_ops;
+
+static char *get_dname(struct dentry *dentry)
+{
+       const char *dname;
+       char *name;
+       int len = dentry->d_name.len;
+
+       dname = dentry->d_name.name;
+       name = kmalloc(len + 1, GFP_KERNEL);
+       if (!name)
+               return NULL;
+       memcpy(name, dname, len);
+       name[len] = 0;
+       return name;
+}
+
+static int tracefs_syscall_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode)
+{
+       char *name;
+       int ret;
+
+       name = get_dname(dentry);
+       if (!name)
+               return -ENOMEM;
+
+       /*
+        * The mkdir call can call the generic functions that create
+        * the files within the tracefs system. It is up to the individual
+        * mkdir routine to handle races.
+        */
+       mutex_unlock(&inode->i_mutex);
+       ret = tracefs_ops.mkdir(name);
+       mutex_lock(&inode->i_mutex);
+
+       kfree(name);
+
+       return ret;
+}
+
+static int tracefs_syscall_rmdir(struct inode *inode, struct dentry *dentry)
+{
+       char *name;
+       int ret;
+
+       name = get_dname(dentry);
+       if (!name)
+               return -ENOMEM;
+
+       /*
+        * The rmdir call can call the generic functions that create
+        * the files within the tracefs system. It is up to the individual
+        * rmdir routine to handle races.
+        * This time we need to unlock not only the parent (inode) but
+        * also the directory that is being deleted.
+        */
+       mutex_unlock(&inode->i_mutex);
+       mutex_unlock(&dentry->d_inode->i_mutex);
+
+       ret = tracefs_ops.rmdir(name);
+
+       mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
+       mutex_lock(&dentry->d_inode->i_mutex);
+
+       kfree(name);
+
+       return ret;
+}
+
+static const struct inode_operations tracefs_dir_inode_operations = {
+       .lookup         = simple_lookup,
+       .mkdir          = tracefs_syscall_mkdir,
+       .rmdir          = tracefs_syscall_rmdir,
+};
+
 static struct inode *tracefs_get_inode(struct super_block *sb)
 {
        struct inode *inode = new_inode(sb);
@@ -334,6 +412,31 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode,
        return end_creating(dentry);
 }
 
+static struct dentry *__create_dir(const char *name, struct dentry *parent,
+                                  const struct inode_operations *ops)
+{
+       struct dentry *dentry = start_creating(name, parent);
+       struct inode *inode;
+
+       if (IS_ERR(dentry))
+               return NULL;
+
+       inode = tracefs_get_inode(dentry->d_sb);
+       if (unlikely(!inode))
+               return failed_creating(dentry);
+
+       inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+       inode->i_op = ops;
+       inode->i_fop = &simple_dir_operations;
+
+       /* directory inodes start off with i_nlink == 2 (for "." entry) */
+       inc_nlink(inode);
+       d_instantiate(dentry, inode);
+       inc_nlink(dentry->d_parent->d_inode);
+       fsnotify_mkdir(dentry->d_parent->d_inode, dentry);
+       return end_creating(dentry);
+}
+
 /**
  * tracefs_create_dir - create a directory in the tracefs filesystem
  * @name: a pointer to a string containing the name of the directory to
@@ -353,26 +456,44 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode,
  */
 struct dentry *tracefs_create_dir(const char *name, struct dentry *parent)
 {
-       struct dentry *dentry = start_creating(name, parent);
-       struct inode *inode;
+       return __create_dir(name, parent, &simple_dir_inode_operations);
+}
 
-       if (IS_ERR(dentry))
+/**
+ * tracefs_create_instance_dir - create the tracing instances directory
+ * @name: The name of the instances directory to create
+ * @parent: The parent directory that the instances directory will exist
+ * @mkdir: The function to call when a mkdir is performed.
+ * @rmdir: The function to call when a rmdir is performed.
+ *
+ * Only one instances directory is allowed.
+ *
+ * The instances directory is special as it allows for mkdir and rmdir to
+ * to be done by userspace. When a mkdir or rmdir is performed, the inode
+ * locks are released and the methhods passed in (@mkdir and @rmdir) are
+ * called without locks and with the name of the directory being created
+ * within the instances directory.
+ *
+ * Returns the dentry of the instances directory.
+ */
+struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *parent,
+                                         int (*mkdir)(const char *name),
+                                         int (*rmdir)(const char *name))
+{
+       struct dentry *dentry;
+
+       /* Only allow one instance of the instances directory. */
+       if (WARN_ON(tracefs_ops.mkdir || tracefs_ops.rmdir))
                return NULL;
 
-       inode = tracefs_get_inode(dentry->d_sb);
-       if (unlikely(!inode))
-               return failed_creating(dentry);
+       dentry = __create_dir(name, parent, &tracefs_dir_inode_operations);
+       if (!dentry)
+               return NULL;
 
-       inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
-       inode->i_op = &simple_dir_inode_operations;
-       inode->i_fop = &simple_dir_operations;
+       tracefs_ops.mkdir = mkdir;
+       tracefs_ops.rmdir = rmdir;
 
-       /* directory inodes start off with i_nlink == 2 (for "." entry) */
-       inc_nlink(inode);
-       d_instantiate(dentry, inode);
-       inc_nlink(dentry->d_parent->d_inode);
-       fsnotify_mkdir(dentry->d_parent->d_inode, dentry);
-       return end_creating(dentry);
+       return dentry;
 }
 
 static inline int tracefs_positive(struct dentry *dentry)