sysfs: fix race condition around sd->s_dentry, take#2
Allowing attribute and symlink dentries to be reclaimed means sd->s_dentry can change dynamically. However, updates to the field are unsynchronized leading to race conditions. This patch adds sysfs_lock and use it to synchronize updates to sd->s_dentry. Due to the locking around ->d_iput, the check in sysfs_drop_dentry() is complex. sysfs_lock only protect sd->s_dentry pointer itself. The validity of the dentry is protected by dcache_lock, so whether dentry is alive or not can only be tested while holding both locks. This is minimal backport of sysfs_drop_dentry() rewrite in devel branch. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
6aa054aadf
commit
dd14cbc994
@@ -246,9 +246,23 @@ static inline void orphan_all_buffers(struct inode *node)
|
||||
*/
|
||||
void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
|
||||
{
|
||||
struct dentry * dentry = sd->s_dentry;
|
||||
struct dentry *dentry = NULL;
|
||||
struct inode *inode;
|
||||
|
||||
/* We're not holding a reference to ->s_dentry dentry but the
|
||||
* field will stay valid as long as sysfs_lock is held.
|
||||
*/
|
||||
spin_lock(&sysfs_lock);
|
||||
spin_lock(&dcache_lock);
|
||||
|
||||
/* dget dentry if it's still alive */
|
||||
if (sd->s_dentry && sd->s_dentry->d_inode)
|
||||
dentry = dget_locked(sd->s_dentry);
|
||||
|
||||
spin_unlock(&dcache_lock);
|
||||
spin_unlock(&sysfs_lock);
|
||||
|
||||
/* drop dentry */
|
||||
if (dentry) {
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
@@ -268,6 +282,8 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
}
|
||||
|
||||
dput(dentry);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user