Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/roland/infiniband
[pandora-kernel.git] / fs / configfs / dir.c
index 5638c8f..5f95218 100644 (file)
@@ -505,13 +505,15 @@ static int populate_groups(struct config_group *group)
        int i;
 
        if (group->default_groups) {
-               /* FYI, we're faking mkdir here
+               /*
+                * FYI, we're faking mkdir here
                 * I'm not sure we need this semaphore, as we're called
                 * from our parent's mkdir.  That holds our parent's
                 * i_mutex, so afaik lookup cannot continue through our
                 * parent to find us, let alone mess with our tree.
                 * That said, taking our i_mutex is closer to mkdir
-                * emulation, and shouldn't hurt. */
+                * emulation, and shouldn't hurt.
+                */
                mutex_lock(&dentry->d_inode->i_mutex);
 
                for (i = 0; group->default_groups[i]; i++) {
@@ -546,20 +548,34 @@ static void unlink_obj(struct config_item *item)
 
                item->ci_group = NULL;
                item->ci_parent = NULL;
+
+               /* Drop the reference for ci_entry */
                config_item_put(item);
 
+               /* Drop the reference for ci_parent */
                config_group_put(group);
        }
 }
 
 static void link_obj(struct config_item *parent_item, struct config_item *item)
 {
-       /* Parent seems redundant with group, but it makes certain
-        * traversals much nicer. */
+       /*
+        * Parent seems redundant with group, but it makes certain
+        * traversals much nicer.
+        */
        item->ci_parent = parent_item;
+
+       /*
+        * We hold a reference on the parent for the child's ci_parent
+        * link.
+        */
        item->ci_group = config_group_get(to_config_group(parent_item));
        list_add_tail(&item->ci_entry, &item->ci_group->cg_children);
 
+       /*
+        * We hold a reference on the child for ci_entry on the parent's
+        * cg_children
+        */
        config_item_get(item);
 }
 
@@ -684,6 +700,10 @@ static void client_drop_item(struct config_item *parent_item,
        type = parent_item->ci_type;
        BUG_ON(!type);
 
+       /*
+        * If ->drop_item() exists, it is responsible for the
+        * config_item_put().
+        */
        if (type->ct_group_ops && type->ct_group_ops->drop_item)
                type->ct_group_ops->drop_item(to_config_group(parent_item),
                                                item);
@@ -694,23 +714,28 @@ static void client_drop_item(struct config_item *parent_item,
 
 static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
-       int ret;
+       int ret, module_got = 0;
        struct config_group *group;
        struct config_item *item;
        struct config_item *parent_item;
        struct configfs_subsystem *subsys;
        struct configfs_dirent *sd;
        struct config_item_type *type;
-       struct module *owner;
+       struct module *owner = NULL;
        char *name;
 
-       if (dentry->d_parent == configfs_sb->s_root)
-               return -EPERM;
+       if (dentry->d_parent == configfs_sb->s_root) {
+               ret = -EPERM;
+               goto out;
+       }
 
        sd = dentry->d_parent->d_fsdata;
-       if (!(sd->s_type & CONFIGFS_USET_DIR))
-               return -EPERM;
+       if (!(sd->s_type & CONFIGFS_USET_DIR)) {
+               ret = -EPERM;
+               goto out;
+       }
 
+       /* Get a working ref for the duration of this function */
        parent_item = configfs_get_config_item(dentry->d_parent);
        type = parent_item->ci_type;
        subsys = to_config_group(parent_item)->cg_subsys;
@@ -719,15 +744,16 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (!type || !type->ct_group_ops ||
            (!type->ct_group_ops->make_group &&
             !type->ct_group_ops->make_item)) {
-               config_item_put(parent_item);
-               return -EPERM;  /* What lack-of-mkdir returns */
+               ret = -EPERM;  /* Lack-of-mkdir returns -EPERM */
+               goto out_put;
        }
 
        name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL);
        if (!name) {
-               config_item_put(parent_item);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out_put;
        }
+
        snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name);
 
        down(&subsys->su_sem);
@@ -748,40 +774,67 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 
        kfree(name);
        if (!item) {
-               config_item_put(parent_item);
-               return -ENOMEM;
+               /*
+                * If item == NULL, then link_obj() was never called.
+                * There are no extra references to clean up.
+                */
+               ret = -ENOMEM;
+               goto out_put;
        }
 
-       ret = -EINVAL;
+       /*
+        * link_obj() has been called (via link_group() for groups).
+        * From here on out, errors must clean that up.
+        */
+
        type = item->ci_type;
-       if (type) {
-               owner = type->ct_owner;
-               if (try_module_get(owner)) {
-                       if (group) {
-                               ret = configfs_attach_group(parent_item,
-                                                           item,
-                                                           dentry);
-                       } else {
-                               ret = configfs_attach_item(parent_item,
-                                                          item,
-                                                          dentry);
-                       }
+       if (!type) {
+               ret = -EINVAL;
+               goto out_unlink;
+       }
 
-                       if (ret) {
-                               down(&subsys->su_sem);
-                               if (group)
-                                       unlink_group(group);
-                               else
-                                       unlink_obj(item);
-                               client_drop_item(parent_item, item);
-                               up(&subsys->su_sem);
+       owner = type->ct_owner;
+       if (!try_module_get(owner)) {
+               ret = -EINVAL;
+               goto out_unlink;
+       }
 
-                               config_item_put(parent_item);
-                               module_put(owner);
-                       }
-               }
+       /*
+        * I hate doing it this way, but if there is
+        * an error,  module_put() probably should
+        * happen after any cleanup.
+        */
+       module_got = 1;
+
+       if (group)
+               ret = configfs_attach_group(parent_item, item, dentry);
+       else
+               ret = configfs_attach_item(parent_item, item, dentry);
+
+out_unlink:
+       if (ret) {
+               /* Tear down everything we built up */
+               down(&subsys->su_sem);
+               if (group)
+                       unlink_group(group);
+               else
+                       unlink_obj(item);
+               client_drop_item(parent_item, item);
+               up(&subsys->su_sem);
+
+               if (module_got)
+                       module_put(owner);
        }
 
+out_put:
+       /*
+        * link_obj()/link_group() took a reference from child->parent,
+        * so the parent is safely pinned.  We can drop our working
+        * reference.
+        */
+       config_item_put(parent_item);
+
+out:
        return ret;
 }
 
@@ -801,6 +854,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (sd->s_type & CONFIGFS_USET_DEFAULT)
                return -EPERM;
 
+       /* Get a working ref until we have the child */
        parent_item = configfs_get_config_item(dentry->d_parent);
        subsys = to_config_group(parent_item)->cg_subsys;
        BUG_ON(!subsys);
@@ -817,6 +871,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
                return ret;
        }
 
+       /* Get a working ref for the duration of this function */
        item = configfs_get_config_item(dentry);
 
        /* Drop reference from above, item already holds one. */