sysfs: Rewrite sysfs_drop_dentry.
authorEric W. Biederman <ebiederm@xmission.com>
Mon, 20 Aug 2007 12:36:30 +0000 (21:36 +0900)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 12 Oct 2007 21:51:08 +0000 (14:51 -0700)
Currently we find the dentry to drop by looking at sd->s_dentry.
We can just as easily accomplish the same task by looking up the
sysfs inode and finding all of the dentries from there, with the
added bonus that we don't need to play with the sysfs_assoc_lock.

Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Tejun Heo <htejun@gmail.com>
Cc: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
fs/sysfs/dir.c

index 953e8432b0ae0b66d4eba3924dab346b3f89d4ca..1af963e66e3cc8232258d8fca21671963168717e 100644 (file)
@@ -565,50 +565,49 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
  *     Drop dentry for @sd.  @sd must have been unlinked from its
  *     parent on entry to this function such that it can't be looked
  *     up anymore.
- *
- *     @sd->s_dentry which is protected with sysfs_assoc_lock points
- *     to the currently associated dentry but we're not holding a
- *     reference to it and racing with dput().  Grab dcache_lock and
- *     verify dentry before dropping it.  If @sd->s_dentry is NULL or
- *     dput() beats us, no need to bother.
  */
 static void sysfs_drop_dentry(struct sysfs_dirent *sd)
 {
-       struct dentry *dentry = NULL;
        struct inode *inode;
+       struct dentry *dentry;
+
+       inode = ilookup(sysfs_sb, sd->s_ino);
+       if (!inode)
+               return;
 
-       /* We're not holding a reference to ->s_dentry dentry but the
-        * field will stay valid as long as sysfs_assoc_lock is held.
+       /* Drop any existing dentries associated with sd.
+        *
+        * For the dentry to be properly freed we need to grab a
+        * reference to the dentry under the dcache lock,  unhash it,
+        * and then put it.  The playing with the dentry count allows
+        * dput to immediately free the dentry  if it is not in use.
         */
-       spin_lock(&sysfs_assoc_lock);
+repeat:
        spin_lock(&dcache_lock);
-
-       /* drop dentry if it's there and dput() didn't kill it yet */
-       if (sd->s_dentry && sd->s_dentry->d_inode) {
-               dentry = dget_locked(sd->s_dentry);
+       list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
+               if (d_unhashed(dentry))
+                       continue;
+               dget_locked(dentry);
                spin_lock(&dentry->d_lock);
                __d_drop(dentry);
                spin_unlock(&dentry->d_lock);
+               spin_unlock(&dcache_lock);
+               dput(dentry);
+               goto repeat;
        }
-
        spin_unlock(&dcache_lock);
-       spin_unlock(&sysfs_assoc_lock);
-
-       dput(dentry);
 
        /* adjust nlink and update timestamp */
-       inode = ilookup(sysfs_sb, sd->s_ino);
-       if (inode) {
-               mutex_lock(&inode->i_mutex);
+       mutex_lock(&inode->i_mutex);
 
-               inode->i_ctime = CURRENT_TIME;
+       inode->i_ctime = CURRENT_TIME;
+       drop_nlink(inode);
+       if (sysfs_type(sd) == SYSFS_DIR)
                drop_nlink(inode);
-               if (sysfs_type(sd) == SYSFS_DIR)
-                       drop_nlink(inode);
 
-               mutex_unlock(&inode->i_mutex);
-               iput(inode);
-       }
+       mutex_unlock(&inode->i_mutex);
+
+       iput(inode);
 }
 
 /**