diff options
author | Tejun Heo <htejun@gmail.com> | 2007-06-11 01:04:01 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-06-12 19:08:47 -0400 |
commit | dd14cbc994709a1c5a64ed3621f583c49a27e521 (patch) | |
tree | e48d38b7450661907c7b75490504c7f70b04d6cc /fs/sysfs/inode.c | |
parent | 6aa054aadfea613a437ad0b15d38eca2b963fc0a (diff) |
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/inode.c')
-rw-r--r-- | fs/sysfs/inode.c | 18 |
1 files changed, 17 insertions, 1 deletions
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 38bbe071cc15..5266eec15f6e 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c | |||
@@ -246,9 +246,23 @@ static inline void orphan_all_buffers(struct inode *node) | |||
246 | */ | 246 | */ |
247 | void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) | 247 | void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) |
248 | { | 248 | { |
249 | struct dentry * dentry = sd->s_dentry; | 249 | struct dentry *dentry = NULL; |
250 | struct inode *inode; | 250 | struct inode *inode; |
251 | 251 | ||
252 | /* We're not holding a reference to ->s_dentry dentry but the | ||
253 | * field will stay valid as long as sysfs_lock is held. | ||
254 | */ | ||
255 | spin_lock(&sysfs_lock); | ||
256 | spin_lock(&dcache_lock); | ||
257 | |||
258 | /* dget dentry if it's still alive */ | ||
259 | if (sd->s_dentry && sd->s_dentry->d_inode) | ||
260 | dentry = dget_locked(sd->s_dentry); | ||
261 | |||
262 | spin_unlock(&dcache_lock); | ||
263 | spin_unlock(&sysfs_lock); | ||
264 | |||
265 | /* drop dentry */ | ||
252 | if (dentry) { | 266 | if (dentry) { |
253 | spin_lock(&dcache_lock); | 267 | spin_lock(&dcache_lock); |
254 | spin_lock(&dentry->d_lock); | 268 | spin_lock(&dentry->d_lock); |
@@ -268,6 +282,8 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) | |||
268 | spin_unlock(&dentry->d_lock); | 282 | spin_unlock(&dentry->d_lock); |
269 | spin_unlock(&dcache_lock); | 283 | spin_unlock(&dcache_lock); |
270 | } | 284 | } |
285 | |||
286 | dput(dentry); | ||
271 | } | 287 | } |
272 | } | 288 | } |
273 | 289 | ||