Commit fb6896da authored by Tejun Heo's avatar Tejun Heo Committed by Greg Kroah-Hartman

sysfs: restructure add/remove paths and fix inode update

The original add/remove code had the following problems.

* parent's timestamps are updated on dentry instantiation.  this is
  incorrect with reclaimable files.

* updating parent's timestamps isn't synchronized.

* parent nlink update assumes the inode is accessible which won't be
  true once directory dentries are made reclaimable.

This patch restructures add/remove paths to resolve the above
problems.  Add/removal are done in the following steps.

1. sysfs_addrm_start() : acquire locks including sysfs_mutex and other
   resources.

2-a. sysfs_add_one() : add new sd.  linking the new sd into the
     children list is caller's responsibility.

2-b. sysfs_remove_one() : remove a sd.  unlinking the sd from the
     children list is caller's responsibility.

3. sysfs_addrm_finish() : release all resources and clean up.

Steps 2-a and/or 2-b can be repeated multiple times.

Parent's inode is looked up during sysfs_addrm_start().  If available
(always at the moment), it's pinned and nlink is updated as sd's are
added and removed.  Timestamps are updated during finish if any sd has
been added or removed.  If parent's inode is not available during
start, sysfs_mutex ensures that parent inode is not created till
add/remove is complete.

All the complexity is contained inside the helper functions.
Especially, dentry/inode handling is properly hidden from the rest of
sysfs which now mostly operate on sysfs_dirents.  As an added bonus,
codes which use these helpers to add and remove sysfs_dirents are now
more structured and simpler.
Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 3007e997
This diff is collapsed.
......@@ -416,6 +416,7 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
int type)
{
umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG;
struct sysfs_addrm_cxt acxt;
struct sysfs_dirent *sd;
sd = sysfs_new_dirent(attr->name, mode, type);
......@@ -423,20 +424,18 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
return -ENOMEM;
sd->s_elem.attr.attr = (void *)attr;
mutex_lock(&sysfs_mutex);
sysfs_addrm_start(&acxt, dir_sd);
if (!sysfs_find_dirent(dir_sd, attr->name)) {
sysfs_attach_dirent(sd, dir_sd, NULL);
sd = NULL;
sysfs_add_one(&acxt, sd);
sysfs_link_sibling(sd);
}
mutex_unlock(&sysfs_mutex);
if (sysfs_addrm_finish(&acxt))
return 0;
if (sd) {
sysfs_put(sd);
return -EEXIST;
}
return 0;
sysfs_put(sd);
return -EEXIST;
}
......
......@@ -191,15 +191,9 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode)
{
BUG_ON(!dentry || dentry->d_inode);
if (inode->i_state & I_NEW) {
if (inode->i_state & I_NEW)
unlock_new_inode(inode);
if (dentry->d_parent && dentry->d_parent->d_inode) {
struct inode *p_inode = dentry->d_parent->d_inode;
p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME;
}
}
d_instantiate(dentry, inode);
}
......@@ -220,7 +214,6 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode)
void sysfs_drop_dentry(struct sysfs_dirent *sd)
{
struct dentry *dentry = NULL;
struct timespec curtime;
struct inode *inode;
/* We're not holding a reference to ->s_dentry dentry but the
......@@ -246,13 +239,11 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd)
dput(dentry);
/* adjust nlink and update timestamp */
curtime = CURRENT_TIME;
inode = ilookup(sysfs_sb, sd->s_ino);
if (inode) {
mutex_lock(&inode->i_mutex);
inode->i_ctime = curtime;
inode->i_ctime = CURRENT_TIME;
drop_nlink(inode);
if (sysfs_type(sd) == SYSFS_DIR)
drop_nlink(inode);
......@@ -260,30 +251,17 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd)
mutex_unlock(&inode->i_mutex);
iput(inode);
}
/* adjust nlink and udpate timestamp of the parent */
inode = ilookup(sysfs_sb, sd->s_parent->s_ino);
if (inode) {
mutex_lock(&inode->i_mutex);
inode->i_ctime = inode->i_mtime = curtime;
if (sysfs_type(sd) == SYSFS_DIR)
drop_nlink(inode);
mutex_unlock(&inode->i_mutex);
iput(inode);
}
}
int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name)
{
struct sysfs_addrm_cxt acxt;
struct sysfs_dirent **pos, *sd;
int found = 0;
if (!dir_sd)
return -ENOENT;
mutex_lock(&sysfs_mutex);
sysfs_addrm_start(&acxt, dir_sd);
for (pos = &dir_sd->s_children; *pos; pos = &(*pos)->s_sibling) {
sd = *pos;
......@@ -291,22 +269,14 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name)
if (!sysfs_type(sd))
continue;
if (!strcmp(sd->s_name, name)) {
sd->s_flags |= SYSFS_FLAG_REMOVED;
*pos = sd->s_sibling;
sd->s_sibling = NULL;
found = 1;
sysfs_remove_one(&acxt, sd);
break;
}
}
mutex_unlock(&sysfs_mutex);
if (!found)
return -ENOENT;
sysfs_drop_dentry(sd);
sysfs_deactivate(sd);
sysfs_put(sd);
return 0;
if (sysfs_addrm_finish(&acxt))
return 0;
return -ENOENT;
}
......@@ -55,6 +55,7 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char
struct sysfs_dirent *parent_sd = NULL;
struct sysfs_dirent *target_sd = NULL;
struct sysfs_dirent *sd = NULL;
struct sysfs_addrm_cxt acxt;
int error;
BUG_ON(!name);
......@@ -87,17 +88,18 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char
goto out_put;
sd->s_elem.symlink.target_sd = target_sd;
mutex_lock(&sysfs_mutex);
error = -EEXIST;
if (sysfs_find_dirent(parent_sd, name))
goto out_unlock;
sysfs_attach_dirent(sd, parent_sd, NULL);
mutex_unlock(&sysfs_mutex);
sysfs_addrm_start(&acxt, parent_sd);
return 0;
if (!sysfs_find_dirent(parent_sd, name)) {
sysfs_add_one(&acxt, sd);
sysfs_link_sibling(sd);
}
out_unlock:
mutex_unlock(&sysfs_mutex);
if (sysfs_addrm_finish(&acxt))
return 0;
error = -EEXIST;
/* fall through */
out_put:
sysfs_put(target_sd);
sysfs_put(sd);
......
......@@ -44,14 +44,29 @@ struct sysfs_dirent {
#define SD_DEACTIVATED_BIAS INT_MIN
struct sysfs_addrm_cxt {
struct sysfs_dirent *parent_sd;
struct inode *parent_inode;
struct sysfs_dirent *removed;
int cnt;
};
extern struct vfsmount * sysfs_mount;
extern struct kmem_cache *sysfs_dir_cachep;
extern void sysfs_link_sibling(struct sysfs_dirent *sd);
extern void sysfs_unlink_sibling(struct sysfs_dirent *sd);
extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd);
extern void sysfs_put_active(struct sysfs_dirent *sd);
extern struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd);
extern void sysfs_put_active_two(struct sysfs_dirent *sd);
extern void sysfs_deactivate(struct sysfs_dirent *sd);
extern void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
struct sysfs_dirent *parent_sd);
extern void sysfs_add_one(struct sysfs_addrm_cxt *acxt,
struct sysfs_dirent *sd);
extern void sysfs_remove_one(struct sysfs_addrm_cxt *acxt,
struct sysfs_dirent *sd);
extern int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt);
extern void sysfs_delete_inode(struct inode *inode);
extern void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode);
......@@ -65,9 +80,6 @@ extern struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
const unsigned char *name);
extern struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode,
int type);
extern void sysfs_attach_dirent(struct sysfs_dirent *sd,
struct sysfs_dirent *parent_sd,
struct dentry *dentry);
extern int sysfs_add_file(struct sysfs_dirent *dir_sd,
const struct attribute *attr, int type);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment