diff options
author | Tejun Heo <htejun@gmail.com> | 2007-06-11 14:04:01 +0900 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-06-12 16:08:47 -0700 |
commit | dd14cbc994709a1c5a64ed3621f583c49a27e521 (patch) | |
tree | e48d38b7450661907c7b75490504c7f70b04d6cc /fs/sysfs/dir.c | |
parent | 6aa054aadfea613a437ad0b15d38eca2b963fc0a (diff) | |
download | linux-dd14cbc994709a1c5a64ed3621f583c49a27e521.tar.bz2 |
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>
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r-- | fs/sysfs/dir.c | 22 |
1 files changed, 20 insertions, 2 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 17a819151b91..c4342a019972 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -13,14 +13,26 @@ #include "sysfs.h" DECLARE_RWSEM(sysfs_rename_sem); +spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED; static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) { struct sysfs_dirent * sd = dentry->d_fsdata; if (sd) { - BUG_ON(sd->s_dentry != dentry); - sd->s_dentry = NULL; + /* sd->s_dentry is protected with sysfs_lock. This + * allows sysfs_drop_dentry() to dereference it. + */ + spin_lock(&sysfs_lock); + + /* The dentry might have been deleted or another + * lookup could have happened updating sd->s_dentry to + * point the new dentry. Ignore if it isn't pointing + * to this dentry. + */ + if (sd->s_dentry == dentry) + sd->s_dentry = NULL; + spin_unlock(&sysfs_lock); sysfs_put(sd); } iput(inode); @@ -247,7 +259,10 @@ static int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry) } dentry->d_fsdata = sysfs_get(sd); + /* protect sd->s_dentry against sysfs_d_iput */ + spin_lock(&sysfs_lock); sd->s_dentry = dentry; + spin_unlock(&sysfs_lock); error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init); if (error) { sysfs_put(sd); @@ -269,7 +284,10 @@ static int sysfs_attach_link(struct sysfs_dirent * sd, struct dentry * dentry) int err = 0; dentry->d_fsdata = sysfs_get(sd); + /* protect sd->s_dentry against sysfs_d_iput */ + spin_lock(&sysfs_lock); sd->s_dentry = dentry; + spin_unlock(&sysfs_lock); err = sysfs_create(dentry, S_IFLNK|S_IRWXUGO, init_symlink); if (!err) { dentry->d_op = &sysfs_dentry_ops; |