sysfs: add sysfs_dirent->s_parent
authorTejun Heo <htejun@gmail.com>
Wed, 13 Jun 2007 18:45:14 +0000 (03:45 +0900)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 11 Jul 2007 23:09:04 +0000 (16:09 -0700)
Add sysfs_dirent->s_parent.  With this patch, each sd points to and
holds a reference to its parent.  This allows walking sysfs tree
without referencing sd->s_dentry which can go away anytime if the user
doesn't control when it's deleted.

sd->s_parent is initialized and parent is referenced in
sysfs_attach_dirent().  Reference to parent is released when the sd is
released, so as long as reference to a sd is held, s_parent can be
followed.

dentry walk in sysfs_readdir() is convereted to s_parent walk.

This will be used to reimplement symlink such that it uses only
sysfs_dirent tree.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
fs/sysfs/dir.c
fs/sysfs/mount.c
fs/sysfs/sysfs.h

index f16aa7e..5d50e1d 100644 (file)
@@ -47,6 +47,11 @@ static void sysfs_free_ino(ino_t ino)
 
 void release_sysfs_dirent(struct sysfs_dirent * sd)
 {
+       struct sysfs_dirent *parent_sd;
+
+ repeat:
+       parent_sd = sd->s_parent;
+
        if (sd->s_type & SYSFS_KOBJ_LINK) {
                struct sysfs_symlink * sl = sd->s_element;
                kfree(sl->link_name);
@@ -56,6 +61,10 @@ void release_sysfs_dirent(struct sysfs_dirent * sd)
        kfree(sd->s_iattr);
        sysfs_free_ino(sd->s_ino);
        kmem_cache_free(sysfs_dir_cachep, sd);
+
+       sd = parent_sd;
+       if (sd && atomic_dec_and_test(&sd->s_count))
+               goto repeat;
 }
 
 static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
@@ -119,8 +128,10 @@ void sysfs_attach_dirent(struct sysfs_dirent *sd,
                dentry->d_op = &sysfs_dentry_ops;
        }
 
-       if (parent_sd)
+       if (parent_sd) {
+               sd->s_parent = sysfs_get(parent_sd);
                list_add(&sd->s_sibling, &parent_sd->s_children);
+       }
 }
 
 /*
@@ -571,7 +582,10 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
                        i++;
                        /* fallthrough */
                case 1:
-                       ino = parent_ino(dentry);
+                       if (parent_sd->s_parent)
+                               ino = parent_sd->s_parent->s_ino;
+                       else
+                               ino = parent_sd->s_ino;
                        if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
                                break;
                        filp->f_pos++;
@@ -688,13 +702,13 @@ int sysfs_make_shadowed_dir(struct kobject *kobj,
 
 struct dentry *sysfs_create_shadow_dir(struct kobject *kobj)
 {
+       struct dentry *dir = kobj->dentry;
+       struct inode *inode = dir->d_inode;
+       struct dentry *parent = dir->d_parent;
+       struct sysfs_dirent *parent_sd = parent->d_fsdata;
+       struct dentry *shadow;
        struct sysfs_dirent *sd;
-       struct dentry *parent, *dir, *shadow;
-       struct inode *inode;
 
-       dir = kobj->dentry;
-       inode = dir->d_inode;
-       parent = dir->d_parent;
        shadow = ERR_PTR(-EINVAL);
        if (!sysfs_is_shadowed_inode(inode))
                goto out;
@@ -706,6 +720,8 @@ struct dentry *sysfs_create_shadow_dir(struct kobject *kobj)
        sd = sysfs_new_dirent(kobj, inode->i_mode, SYSFS_DIR);
        if (!sd)
                goto nomem;
+       /* point to parent_sd but don't attach to it */
+       sd->s_parent = sysfs_get(parent_sd);
        sysfs_attach_dirent(sd, NULL, shadow);
 
        d_instantiate(shadow, igrab(inode));
index 00ab912..31c1fc6 100644 (file)
@@ -28,6 +28,7 @@ static const struct super_operations sysfs_ops = {
 };
 
 static struct sysfs_dirent sysfs_root = {
+       .s_count        = ATOMIC_INIT(1),
        .s_sibling      = LIST_HEAD_INIT(sysfs_root.s_sibling),
        .s_children     = LIST_HEAD_INIT(sysfs_root.s_children),
        .s_element      = NULL,
index f8f49cc..ce05d6f 100644 (file)
@@ -1,5 +1,6 @@
 struct sysfs_dirent {
        atomic_t                s_count;
+       struct sysfs_dirent     * s_parent;
        struct list_head        s_sibling;
        struct list_head        s_children;
        void                    * s_element;