Skip to content

Commit

Permalink
[PATCH] configfs: Pin configfs subsystems separately from new config_…
Browse files Browse the repository at this point in the history
…items.

configfs_mkdir() creates a new item by calling its parent's
->make_item/group() functions.  Once that object is created,
configfs_mkdir() calls try_module_get() on the new item's module.  If it
succeeds, the module owning the new item cannot be unloaded, and
configfs is safe to reference the item.

If the item and the subsystem it belongs to are part of the same module,
the subsystem is also pinned.  This is the common case.

However, if the subsystem is made up of multiple modules, this may not
pin the subsystem.  Thus, it would be possible to unload the toplevel
subsystem module while there is still a child item.  Thus, we now
try_module_get() the subsystem's module.  This only really affects
children of the toplevel subsystem group.  Deeper children already have
their parents pinned.

Signed-off-by: Joel Becker <joel.becker@oracle.com>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
  • Loading branch information
Joel Becker authored and Mark Fasheh committed Jul 31, 2008
1 parent 99cefda commit 70526b6
Showing 1 changed file with 33 additions and 9 deletions.
42 changes: 33 additions & 9 deletions fs/configfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -1100,7 +1100,7 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
struct configfs_subsystem *subsys;
struct configfs_dirent *sd;
struct config_item_type *type;
struct module *owner = NULL;
struct module *subsys_owner = NULL, *new_item_owner = NULL;
char *name;

if (dentry->d_parent == configfs_sb->s_root) {
Expand Down Expand Up @@ -1137,10 +1137,25 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
goto out_put;
}

/*
* The subsystem may belong to a different module than the item
* being created. We don't want to safely pin the new item but
* fail to pin the subsystem it sits under.
*/
if (!subsys->su_group.cg_item.ci_type) {
ret = -EINVAL;
goto out_put;
}
subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
if (!try_module_get(subsys_owner)) {
ret = -EINVAL;
goto out_put;
}

name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL);
if (!name) {
ret = -ENOMEM;
goto out_put;
goto out_subsys_put;
}

snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name);
Expand Down Expand Up @@ -1172,7 +1187,7 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
* If ret != 0, then link_obj() was never called.
* There are no extra references to clean up.
*/
goto out_put;
goto out_subsys_put;
}

/*
Expand All @@ -1186,8 +1201,8 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
goto out_unlink;
}

owner = type->ct_owner;
if (!try_module_get(owner)) {
new_item_owner = type->ct_owner;
if (!try_module_get(new_item_owner)) {
ret = -EINVAL;
goto out_unlink;
}
Expand Down Expand Up @@ -1236,9 +1251,13 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
mutex_unlock(&subsys->su_mutex);

if (module_got)
module_put(owner);
module_put(new_item_owner);
}

out_subsys_put:
if (ret)
module_put(subsys_owner);

out_put:
/*
* link_obj()/link_group() took a reference from child->parent,
Expand All @@ -1257,7 +1276,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
struct config_item *item;
struct configfs_subsystem *subsys;
struct configfs_dirent *sd;
struct module *owner = NULL;
struct module *subsys_owner = NULL, *dead_item_owner = NULL;
int ret;

if (dentry->d_parent == configfs_sb->s_root)
Expand All @@ -1284,6 +1303,10 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
return -EINVAL;
}

/* configfs_mkdir() shouldn't have allowed this */
BUG_ON(!subsys->su_group.cg_item.ci_type);
subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;

/*
* Ensure that no racing symlink() will make detach_prep() fail while
* the new link is temporarily attached
Expand Down Expand Up @@ -1321,7 +1344,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
config_item_put(parent_item);

if (item->ci_type)
owner = item->ci_type->ct_owner;
dead_item_owner = item->ci_type->ct_owner;

if (sd->s_type & CONFIGFS_USET_DIR) {
configfs_detach_group(item);
Expand All @@ -1343,7 +1366,8 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
/* Drop our reference from above */
config_item_put(item);

module_put(owner);
module_put(dead_item_owner);
module_put(subsys_owner);

return 0;
}
Expand Down

0 comments on commit 70526b6

Please sign in to comment.