{
struct configfs_dirent * sd;
- sd = kmalloc(sizeof(*sd), GFP_KERNEL);
+ sd = kmem_cache_alloc(configfs_dir_cachep, GFP_KERNEL);
if (!sd)
return NULL;
int error;
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
- error = configfs_create(d, mode, init_dir);
+ error = configfs_make_dirent(p->d_fsdata, d, k, mode,
+ CONFIGFS_DIR);
if (!error) {
- error = configfs_make_dirent(p->d_fsdata, d, k, mode,
- CONFIGFS_DIR);
+ error = configfs_create(d, mode, init_dir);
if (!error) {
p->d_inode->i_nlink++;
(d)->d_op = &configfs_dentry_ops;
+ } else {
+ struct configfs_dirent *sd = d->d_fsdata;
+ if (sd) {
+ list_del_init(&sd->s_sibling);
+ configfs_put(sd);
+ }
}
}
return error;
int err = 0;
umode_t mode = S_IFLNK | S_IRWXUGO;
- err = configfs_create(dentry, mode, init_symlink);
+ err = configfs_make_dirent(parent->d_fsdata, dentry, sl, mode,
+ CONFIGFS_ITEM_LINK);
if (!err) {
- err = configfs_make_dirent(parent->d_fsdata, dentry, sl,
- mode, CONFIGFS_ITEM_LINK);
+ err = configfs_create(dentry, mode, init_symlink);
if (!err)
dentry->d_op = &configfs_dentry_ops;
+ else {
+ struct configfs_dirent *sd = dentry->d_fsdata;
+ if (sd) {
+ list_del_init(&sd->s_sibling);
+ configfs_put(sd);
+ }
+ }
}
return err;
}
struct configfs_attribute * attr = sd->s_element;
int error;
+ dentry->d_fsdata = configfs_get(sd);
+ sd->s_dentry = dentry;
error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, init_file);
- if (error)
+ if (error) {
+ configfs_put(sd);
return error;
+ }
dentry->d_op = &configfs_dentry_ops;
- dentry->d_fsdata = configfs_get(sd);
- sd->s_dentry = dentry;
d_rehash(dentry);
return 0;
/*
* Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are
- * attributes and are removed by rmdir(). We recurse, taking i_sem
+ * attributes and are removed by rmdir(). We recurse, taking i_mutex
* on all children that are candidates for default detach. If the
* result is clean, then configfs_detach_group() will handle dropping
- * i_sem. If there is an error, the caller will clean up the i_sem
+ * i_mutex. If there is an error, the caller will clean up the i_mutex
* holders via configfs_detach_rollback().
*/
static int configfs_detach_prep(struct dentry *dentry)
if (sd->s_type & CONFIGFS_NOT_PINNED)
continue;
if (sd->s_type & CONFIGFS_USET_DEFAULT) {
- down(&sd->s_dentry->d_inode->i_sem);
- /* Mark that we've taken i_sem */
+ mutex_lock(&sd->s_dentry->d_inode->i_mutex);
+ /* Mark that we've taken i_mutex */
sd->s_type |= CONFIGFS_USET_DROPPING;
ret = configfs_detach_prep(sd->s_dentry);
}
/*
- * Walk the tree, dropping i_sem wherever CONFIGFS_USET_DROPPING is
+ * Walk the tree, dropping i_mutex wherever CONFIGFS_USET_DROPPING is
* set.
*/
static void configfs_detach_rollback(struct dentry *dentry)
if (sd->s_type & CONFIGFS_USET_DROPPING) {
sd->s_type &= ~CONFIGFS_USET_DROPPING;
- up(&sd->s_dentry->d_inode->i_sem);
+ mutex_unlock(&sd->s_dentry->d_inode->i_mutex);
}
}
}
/*
* From rmdir/unregister, a configfs_detach_prep() pass
- * has taken our i_sem for us. Drop it.
+ * has taken our i_mutex for us. Drop it.
* From mkdir/register cleanup, there is no sem held.
*/
if (sd->s_type & CONFIGFS_USET_DROPPING)
- up(&child->d_inode->i_sem);
+ mutex_unlock(&child->d_inode->i_mutex);
d_delete(child);
dput(child);
int ret = 0;
int i;
- if (group && group->default_groups) {
- /* FYI, we're faking mkdir here
+ if (group->default_groups) {
+ /*
+ * 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_sem, so afaik lookup cannot continue through our
+ * i_mutex, so afaik lookup cannot continue through our
* parent to find us, let alone mess with our tree.
- * That said, taking our i_sem is closer to mkdir
- * emulation, and shouldn't hurt. */
- down(&dentry->d_inode->i_sem);
+ * That said, taking our i_mutex is closer to mkdir
+ * emulation, and shouldn't hurt.
+ */
+ mutex_lock(&dentry->d_inode->i_mutex);
for (i = 0; group->default_groups[i]; i++) {
new_group = group->default_groups[i];
break;
}
- up(&dentry->d_inode->i_sem);
+ mutex_unlock(&dentry->d_inode->i_mutex);
}
if (ret)
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);
}
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);
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;
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);
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;
}
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);
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. */
.symlink = configfs_symlink,
.unlink = configfs_unlink,
.lookup = configfs_lookup,
+ .setattr = configfs_setattr,
};
#if 0
down_write(&configfs_rename_sem);
parent = item->parent->dentry;
- down(&parent->d_inode->i_sem);
+ mutex_lock(&parent->d_inode->i_mutex);
new_dentry = lookup_one_len(new_name, parent, strlen(new_name));
if (!IS_ERR(new_dentry)) {
error = -EEXIST;
dput(new_dentry);
}
- up(&parent->d_inode->i_sem);
+ mutex_unlock(&parent->d_inode->i_mutex);
up_write(&configfs_rename_sem);
return error;
struct dentry * dentry = file->f_dentry;
struct configfs_dirent * parent_sd = dentry->d_fsdata;
- down(&dentry->d_inode->i_sem);
+ mutex_lock(&dentry->d_inode->i_mutex);
file->private_data = configfs_new_dirent(parent_sd, NULL);
- up(&dentry->d_inode->i_sem);
+ mutex_unlock(&dentry->d_inode->i_mutex);
return file->private_data ? 0 : -ENOMEM;
struct dentry * dentry = file->f_dentry;
struct configfs_dirent * cursor = file->private_data;
- down(&dentry->d_inode->i_sem);
+ mutex_lock(&dentry->d_inode->i_mutex);
list_del_init(&cursor->s_sibling);
- up(&dentry->d_inode->i_sem);
+ mutex_unlock(&dentry->d_inode->i_mutex);
release_configfs_dirent(cursor);
{
struct dentry * dentry = file->f_dentry;
- down(&dentry->d_inode->i_sem);
+ mutex_lock(&dentry->d_inode->i_mutex);
switch (origin) {
case 1:
offset += file->f_pos;
if (offset >= 0)
break;
default:
- up(&file->f_dentry->d_inode->i_sem);
+ mutex_unlock(&file->f_dentry->d_inode->i_mutex);
return -EINVAL;
}
if (offset != file->f_pos) {
list_add_tail(&cursor->s_sibling, p);
}
}
- up(&dentry->d_inode->i_sem);
+ mutex_unlock(&dentry->d_inode->i_mutex);
return offset;
}
-struct file_operations configfs_dir_operations = {
+const struct file_operations configfs_dir_operations = {
.open = configfs_dir_open,
.release = configfs_dir_close,
.llseek = configfs_dir_lseek,
sd = configfs_sb->s_root->d_fsdata;
link_group(to_config_group(sd->s_element), group);
- down(&configfs_sb->s_root->d_inode->i_sem);
+ mutex_lock(&configfs_sb->s_root->d_inode->i_mutex);
name.name = group->cg_item.ci_name;
name.len = strlen(name.name);
else
d_delete(dentry);
- up(&configfs_sb->s_root->d_inode->i_sem);
+ mutex_unlock(&configfs_sb->s_root->d_inode->i_mutex);
if (dentry) {
dput(dentry);
return;
}
- down(&configfs_sb->s_root->d_inode->i_sem);
- down(&dentry->d_inode->i_sem);
+ mutex_lock(&configfs_sb->s_root->d_inode->i_mutex);
+ mutex_lock(&dentry->d_inode->i_mutex);
if (configfs_detach_prep(dentry)) {
printk(KERN_ERR "configfs: Tried to unregister non-empty subsystem!\n");
}
configfs_detach_group(&group->cg_item);
dentry->d_inode->i_flags |= S_DEAD;
- up(&dentry->d_inode->i_sem);
+ mutex_unlock(&dentry->d_inode->i_mutex);
d_delete(dentry);
- up(&configfs_sb->s_root->d_inode->i_sem);
+ mutex_unlock(&configfs_sb->s_root->d_inode->i_mutex);
dput(dentry);