diff options
author | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:49:33 -0500 |
---|---|---|
committer | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:50:21 -0500 |
commit | da5029563a0a026c64821b09e8e7b4fd81d3fe1b (patch) | |
tree | 5d5618e0cb382390073377b1be7d0aa76879ac54 /fs/libfs.c | |
parent | b7ab39f631f505edc2bbdb86620d5493f995c9da (diff) |
fs: dcache scale d_unhashed
Protect d_unhashed(dentry) condition with d_lock. This means keeping
DCACHE_UNHASHED bit in synch with hash manipulations.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Diffstat (limited to 'fs/libfs.c')
-rw-r--r-- | fs/libfs.c | 29 |
1 files changed, 20 insertions, 9 deletions
diff --git a/fs/libfs.c b/fs/libfs.c index b9d25d83e228..433e7139c23a 100644 --- a/fs/libfs.c +++ b/fs/libfs.c | |||
@@ -16,6 +16,11 @@ | |||
16 | 16 | ||
17 | #include <asm/uaccess.h> | 17 | #include <asm/uaccess.h> |
18 | 18 | ||
19 | static inline int simple_positive(struct dentry *dentry) | ||
20 | { | ||
21 | return dentry->d_inode && !d_unhashed(dentry); | ||
22 | } | ||
23 | |||
19 | int simple_getattr(struct vfsmount *mnt, struct dentry *dentry, | 24 | int simple_getattr(struct vfsmount *mnt, struct dentry *dentry, |
20 | struct kstat *stat) | 25 | struct kstat *stat) |
21 | { | 26 | { |
@@ -100,8 +105,10 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) | |||
100 | while (n && p != &file->f_path.dentry->d_subdirs) { | 105 | while (n && p != &file->f_path.dentry->d_subdirs) { |
101 | struct dentry *next; | 106 | struct dentry *next; |
102 | next = list_entry(p, struct dentry, d_u.d_child); | 107 | next = list_entry(p, struct dentry, d_u.d_child); |
103 | if (!d_unhashed(next) && next->d_inode) | 108 | spin_lock(&next->d_lock); |
109 | if (simple_positive(next)) | ||
104 | n--; | 110 | n--; |
111 | spin_unlock(&next->d_lock); | ||
105 | p = p->next; | 112 | p = p->next; |
106 | } | 113 | } |
107 | list_add_tail(&cursor->d_u.d_child, p); | 114 | list_add_tail(&cursor->d_u.d_child, p); |
@@ -155,9 +162,13 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
155 | for (p=q->next; p != &dentry->d_subdirs; p=p->next) { | 162 | for (p=q->next; p != &dentry->d_subdirs; p=p->next) { |
156 | struct dentry *next; | 163 | struct dentry *next; |
157 | next = list_entry(p, struct dentry, d_u.d_child); | 164 | next = list_entry(p, struct dentry, d_u.d_child); |
158 | if (d_unhashed(next) || !next->d_inode) | 165 | spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED); |
166 | if (!simple_positive(next)) { | ||
167 | spin_unlock(&next->d_lock); | ||
159 | continue; | 168 | continue; |
169 | } | ||
160 | 170 | ||
171 | spin_unlock(&next->d_lock); | ||
161 | spin_unlock(&dcache_lock); | 172 | spin_unlock(&dcache_lock); |
162 | if (filldir(dirent, next->d_name.name, | 173 | if (filldir(dirent, next->d_name.name, |
163 | next->d_name.len, filp->f_pos, | 174 | next->d_name.len, filp->f_pos, |
@@ -259,20 +270,20 @@ int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *den | |||
259 | return 0; | 270 | return 0; |
260 | } | 271 | } |
261 | 272 | ||
262 | static inline int simple_positive(struct dentry *dentry) | ||
263 | { | ||
264 | return dentry->d_inode && !d_unhashed(dentry); | ||
265 | } | ||
266 | |||
267 | int simple_empty(struct dentry *dentry) | 273 | int simple_empty(struct dentry *dentry) |
268 | { | 274 | { |
269 | struct dentry *child; | 275 | struct dentry *child; |
270 | int ret = 0; | 276 | int ret = 0; |
271 | 277 | ||
272 | spin_lock(&dcache_lock); | 278 | spin_lock(&dcache_lock); |
273 | list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) | 279 | list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) { |
274 | if (simple_positive(child)) | 280 | spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); |
281 | if (simple_positive(child)) { | ||
282 | spin_unlock(&child->d_lock); | ||
275 | goto out; | 283 | goto out; |
284 | } | ||
285 | spin_unlock(&child->d_lock); | ||
286 | } | ||
276 | ret = 1; | 287 | ret = 1; |
277 | out: | 288 | out: |
278 | spin_unlock(&dcache_lock); | 289 | spin_unlock(&dcache_lock); |