diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 953e8432b0ae..1af963e66e3c 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -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; - /* We're not holding a reference to ->s_dentry dentry but the - * field will stay valid as long as sysfs_assoc_lock is held. + inode = ilookup(sysfs_sb, sd->s_ino); + if (!inode) + return; + + /* 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); } /**