Commit fce86bf7 authored by Andrew Morton's avatar Andrew Morton Committed by Greg Kroah-Hartman

[PATCH] sysfs backing store: use sysfs_dirent based tree in file removal

From: Maneesh Soni <maneesh@in.ibm.com>

o This patch uses the sysfs_dirent based tree while removing sysfs files
  and directories. This avoids holding dcache_lock by not using dentry
  based vfs tree. Thus simplyfying the removal logic in sysfs.

o It uses two helper routines sysfs_get_name(), to get the name for
  sysfs element and sysfs_drop_dentry() to delete the dentry given a
  sysfs_dirent.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent c386f08b
...@@ -19,7 +19,7 @@ static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) ...@@ -19,7 +19,7 @@ static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
if (sd) { if (sd) {
BUG_ON(sd->s_dentry != dentry); BUG_ON(sd->s_dentry != dentry);
sd->s_dentry = NULL; sd->s_dentry = NULL;
release_sysfs_dirent(sd); sysfs_put(sd);
} }
iput(inode); iput(inode);
} }
...@@ -61,7 +61,7 @@ int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, ...@@ -61,7 +61,7 @@ int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry,
sd->s_mode = mode; sd->s_mode = mode;
sd->s_type = type; sd->s_type = type;
sd->s_dentry = dentry; sd->s_dentry = dentry;
dentry->d_fsdata = sd; dentry->d_fsdata = sysfs_get(sd);
dentry->d_op = &sysfs_dentry_ops; dentry->d_op = &sysfs_dentry_ops;
return 0; return 0;
...@@ -146,6 +146,7 @@ static void remove_dir(struct dentry * d) ...@@ -146,6 +146,7 @@ static void remove_dir(struct dentry * d)
d_delete(d); d_delete(d);
sd = d->d_fsdata; sd = d->d_fsdata;
list_del_init(&sd->s_sibling); list_del_init(&sd->s_sibling);
sysfs_put(sd);
if (d->d_inode) if (d->d_inode)
simple_rmdir(parent->d_inode,d); simple_rmdir(parent->d_inode,d);
...@@ -173,49 +174,22 @@ void sysfs_remove_subdir(struct dentry * d) ...@@ -173,49 +174,22 @@ void sysfs_remove_subdir(struct dentry * d)
void sysfs_remove_dir(struct kobject * kobj) void sysfs_remove_dir(struct kobject * kobj)
{ {
struct list_head * node;
struct dentry * dentry = dget(kobj->dentry); struct dentry * dentry = dget(kobj->dentry);
struct sysfs_dirent * parent_sd = dentry->d_fsdata;
struct sysfs_dirent * sd, * tmp;
if (!dentry) if (!dentry)
return; return;
pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); pr_debug("sysfs %s: removing dir\n",dentry->d_name.name);
down(&dentry->d_inode->i_sem); down(&dentry->d_inode->i_sem);
list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
spin_lock(&dcache_lock); if (!sd->s_element)
restart: continue;
node = dentry->d_subdirs.next; list_del_init(&sd->s_sibling);
while (node != &dentry->d_subdirs) { sysfs_drop_dentry(sd, dentry);
struct dentry * d = list_entry(node,struct dentry,d_child); sysfs_put(sd);
node = node->next;
pr_debug(" o %s (%d): ",d->d_name.name,atomic_read(&d->d_count));
if (!d_unhashed(d) && (d->d_inode)) {
struct sysfs_dirent * sd = d->d_fsdata;
d = dget_locked(d);
pr_debug("removing");
/**
* Unlink and unhash.
*/
__d_drop(d);
spin_unlock(&dcache_lock);
/* release the target kobject in case of
* a symlink
*/
if (S_ISLNK(d->d_inode->i_mode))
kobject_put(sd->s_element);
list_del_init(&sd->s_sibling);
simple_unlink(dentry->d_inode,d);
dput(d);
pr_debug(" done\n");
spin_lock(&dcache_lock);
/* re-acquired dcache_lock, need to restart */
goto restart;
}
} }
spin_unlock(&dcache_lock);
up(&dentry->d_inode->i_sem); up(&dentry->d_inode->i_sem);
remove_dir(dentry); remove_dir(dentry);
......
...@@ -31,8 +31,8 @@ struct inode * sysfs_new_inode(mode_t mode) ...@@ -31,8 +31,8 @@ struct inode * sysfs_new_inode(mode_t mode)
struct inode * inode = new_inode(sysfs_sb); struct inode * inode = new_inode(sysfs_sb);
if (inode) { if (inode) {
inode->i_mode = mode; inode->i_mode = mode;
inode->i_uid = current->fsuid; inode->i_uid = 0;
inode->i_gid = current->fsgid; inode->i_gid = 0;
inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0; inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
...@@ -68,7 +68,8 @@ int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) ...@@ -68,7 +68,8 @@ int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *))
error = init(inode); error = init(inode);
if (!error) { if (!error) {
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
dget(dentry); /* Extra count - pin the dentry in core */ if (S_ISDIR(mode))
dget(dentry); /* pin only directory dentry in core */
} else } else
iput(inode); iput(inode);
Done: Done:
...@@ -90,35 +91,74 @@ struct dentry * sysfs_get_dentry(struct dentry * parent, const char * name) ...@@ -90,35 +91,74 @@ struct dentry * sysfs_get_dentry(struct dentry * parent, const char * name)
return lookup_hash(&qstr,parent); return lookup_hash(&qstr,parent);
} }
/*
* Get the name for corresponding element represented by the given sysfs_dirent
*/
const unsigned char * sysfs_get_name(struct sysfs_dirent *sd)
{
struct attribute * attr;
struct bin_attribute * bin_attr;
struct sysfs_symlink * sl;
if (!sd || !sd->s_element)
BUG();
switch (sd->s_type) {
case SYSFS_DIR:
/* Always have a dentry so use that */
return sd->s_dentry->d_name.name;
case SYSFS_KOBJ_ATTR:
attr = sd->s_element;
return attr->name;
case SYSFS_KOBJ_BIN_ATTR:
bin_attr = sd->s_element;
return bin_attr->attr.name;
case SYSFS_KOBJ_LINK:
sl = sd->s_element;
return sl->link_name;
}
return NULL;
}
/*
* Unhashes the dentry corresponding to given sysfs_dirent
* Called with parent inode's i_sem held.
*/
void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
{
struct dentry * dentry = sd->s_dentry;
if (dentry) {
spin_lock(&dcache_lock);
if (!(d_unhashed(dentry) && dentry->d_inode)) {
dget_locked(dentry);
__d_drop(dentry);
spin_unlock(&dcache_lock);
simple_unlink(parent->d_inode, dentry);
} else
spin_unlock(&dcache_lock);
}
}
void sysfs_hash_and_remove(struct dentry * dir, const char * name) void sysfs_hash_and_remove(struct dentry * dir, const char * name)
{ {
struct dentry * victim;
struct sysfs_dirent * sd; struct sysfs_dirent * sd;
struct sysfs_dirent * parent_sd = dir->d_fsdata;
down(&dir->d_inode->i_sem); down(&dir->d_inode->i_sem);
victim = sysfs_get_dentry(dir,name); list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
if (!IS_ERR(victim)) { if (!sd->s_element)
/* make sure dentry is really there */ continue;
if (victim->d_inode && if (!strcmp(sysfs_get_name(sd), name)) {
(victim->d_parent->d_inode == dir->d_inode)) {
pr_debug("sysfs: Removing %s (%d)\n", victim->d_name.name,
atomic_read(&victim->d_count));
sd = victim->d_fsdata;
d_drop(victim);
/* release the target kobject in case of
* a symlink
*/
if (S_ISLNK(victim->d_inode->i_mode))
kobject_put(sd->s_element);
list_del_init(&sd->s_sibling); list_del_init(&sd->s_sibling);
simple_unlink(dir->d_inode,victim); sysfs_drop_dentry(sd, dir);
} else sysfs_put(sd);
d_drop(victim); break;
/* }
* Drop reference from sysfs_get_dentry() above.
*/
dput(victim);
} }
up(&dir->d_inode->i_sem); up(&dir->d_inode->i_sem);
} }
......
...@@ -14,6 +14,9 @@ extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); ...@@ -14,6 +14,9 @@ extern void sysfs_hash_and_remove(struct dentry * dir, const char * name);
extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);
extern void sysfs_remove_subdir(struct dentry *); extern void sysfs_remove_subdir(struct dentry *);
extern const unsigned char * sysfs_get_name(struct sysfs_dirent *sd);
extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent);
extern int sysfs_follow_link(struct dentry *, struct nameidata *); extern int sysfs_follow_link(struct dentry *, struct nameidata *);
extern void sysfs_put_link(struct dentry *, struct nameidata *); extern void sysfs_put_link(struct dentry *, struct nameidata *);
extern struct rw_semaphore sysfs_rename_sem; extern struct rw_semaphore sysfs_rename_sem;
...@@ -70,3 +73,18 @@ static inline void release_sysfs_dirent(struct sysfs_dirent * sd) ...@@ -70,3 +73,18 @@ static inline void release_sysfs_dirent(struct sysfs_dirent * sd)
kfree(sd); kfree(sd);
} }
static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd)
{
if (sd) {
WARN_ON(!atomic_read(&sd->s_count));
atomic_inc(&sd->s_count);
}
return sd;
}
static inline void sysfs_put(struct sysfs_dirent * sd)
{
if (atomic_dec_and_test(&sd->s_count))
release_sysfs_dirent(sd);
}
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