diff options
author | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:49:34 -0500 |
---|---|---|
committer | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:50:21 -0500 |
commit | 2fd6b7f50797f2e993eea59e0a0b8c6399c811dc (patch) | |
tree | ce33b94b34844c09103836cf4cfa4364b742f217 /fs | |
parent | da5029563a0a026c64821b09e8e7b4fd81d3fe1b (diff) |
fs: dcache scale subdirs
Protect d_subdirs and d_child with d_lock, except in filesystems that aren't
using dcache_lock for these anyway (eg. using i_mutex).
Note: if we change the locking rule in future so that ->d_child protection is
provided only with ->d_parent->d_lock, it may allow us to reduce some locking.
But it would be an exception to an otherwise regular locking scheme, so we'd
have to see some good results. Probably not worthwhile.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/autofs4/autofs_i.h | 11 | ||||
-rw-r--r-- | fs/autofs4/expire.c | 127 | ||||
-rw-r--r-- | fs/autofs4/root.c | 18 | ||||
-rw-r--r-- | fs/ceph/dir.c | 6 | ||||
-rw-r--r-- | fs/ceph/inode.c | 8 | ||||
-rw-r--r-- | fs/coda/cache.c | 2 | ||||
-rw-r--r-- | fs/dcache.c | 248 | ||||
-rw-r--r-- | fs/libfs.c | 24 | ||||
-rw-r--r-- | fs/ncpfs/dir.c | 3 | ||||
-rw-r--r-- | fs/ncpfs/ncplib_kernel.h | 4 | ||||
-rw-r--r-- | fs/notify/fsnotify.c | 4 |
11 files changed, 302 insertions, 153 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 3912dcf047e5..9d2ae9b30d9f 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h | |||
@@ -254,6 +254,17 @@ static inline int simple_positive(struct dentry *dentry) | |||
254 | return dentry->d_inode && !d_unhashed(dentry); | 254 | return dentry->d_inode && !d_unhashed(dentry); |
255 | } | 255 | } |
256 | 256 | ||
257 | static inline void __autofs4_add_expiring(struct dentry *dentry) | ||
258 | { | ||
259 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); | ||
260 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | ||
261 | if (ino) { | ||
262 | if (list_empty(&ino->expiring)) | ||
263 | list_add(&ino->expiring, &sbi->expiring_list); | ||
264 | } | ||
265 | return; | ||
266 | } | ||
267 | |||
257 | static inline void autofs4_add_expiring(struct dentry *dentry) | 268 | static inline void autofs4_add_expiring(struct dentry *dentry) |
258 | { | 269 | { |
259 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); | 270 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); |
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index ee6402050f13..968c1434af62 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c | |||
@@ -91,24 +91,64 @@ done: | |||
91 | } | 91 | } |
92 | 92 | ||
93 | /* | 93 | /* |
94 | * Calculate next entry in top down tree traversal. | 94 | * Calculate and dget next entry in top down tree traversal. |
95 | * From next_mnt in namespace.c - elegant. | ||
96 | */ | 95 | */ |
97 | static struct dentry *next_dentry(struct dentry *p, struct dentry *root) | 96 | static struct dentry *get_next_positive_dentry(struct dentry *prev, |
97 | struct dentry *root) | ||
98 | { | 98 | { |
99 | struct list_head *next = p->d_subdirs.next; | 99 | struct list_head *next; |
100 | struct dentry *p, *ret; | ||
101 | |||
102 | if (prev == NULL) | ||
103 | return dget(prev); | ||
100 | 104 | ||
105 | spin_lock(&dcache_lock); | ||
106 | relock: | ||
107 | p = prev; | ||
108 | spin_lock(&p->d_lock); | ||
109 | again: | ||
110 | next = p->d_subdirs.next; | ||
101 | if (next == &p->d_subdirs) { | 111 | if (next == &p->d_subdirs) { |
102 | while (1) { | 112 | while (1) { |
103 | if (p == root) | 113 | struct dentry *parent; |
114 | |||
115 | if (p == root) { | ||
116 | spin_unlock(&p->d_lock); | ||
117 | spin_unlock(&dcache_lock); | ||
118 | dput(prev); | ||
104 | return NULL; | 119 | return NULL; |
120 | } | ||
121 | |||
122 | parent = p->d_parent; | ||
123 | if (!spin_trylock(&parent->d_lock)) { | ||
124 | spin_unlock(&p->d_lock); | ||
125 | cpu_relax(); | ||
126 | goto relock; | ||
127 | } | ||
128 | spin_unlock(&p->d_lock); | ||
105 | next = p->d_u.d_child.next; | 129 | next = p->d_u.d_child.next; |
106 | if (next != &p->d_parent->d_subdirs) | 130 | p = parent; |
131 | if (next != &parent->d_subdirs) | ||
107 | break; | 132 | break; |
108 | p = p->d_parent; | ||
109 | } | 133 | } |
110 | } | 134 | } |
111 | return list_entry(next, struct dentry, d_u.d_child); | 135 | ret = list_entry(next, struct dentry, d_u.d_child); |
136 | |||
137 | spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED); | ||
138 | /* Negative dentry - try next */ | ||
139 | if (!simple_positive(ret)) { | ||
140 | spin_unlock(&ret->d_lock); | ||
141 | p = ret; | ||
142 | goto again; | ||
143 | } | ||
144 | dget_dlock(ret); | ||
145 | spin_unlock(&ret->d_lock); | ||
146 | spin_unlock(&p->d_lock); | ||
147 | spin_unlock(&dcache_lock); | ||
148 | |||
149 | dput(prev); | ||
150 | |||
151 | return ret; | ||
112 | } | 152 | } |
113 | 153 | ||
114 | /* | 154 | /* |
@@ -158,22 +198,11 @@ static int autofs4_tree_busy(struct vfsmount *mnt, | |||
158 | if (!simple_positive(top)) | 198 | if (!simple_positive(top)) |
159 | return 1; | 199 | return 1; |
160 | 200 | ||
161 | spin_lock(&dcache_lock); | 201 | p = NULL; |
162 | for (p = top; p; p = next_dentry(p, top)) { | 202 | while ((p = get_next_positive_dentry(p, top))) { |
163 | spin_lock(&p->d_lock); | ||
164 | /* Negative dentry - give up */ | ||
165 | if (!simple_positive(p)) { | ||
166 | spin_unlock(&p->d_lock); | ||
167 | continue; | ||
168 | } | ||
169 | |||
170 | DPRINTK("dentry %p %.*s", | 203 | DPRINTK("dentry %p %.*s", |
171 | p, (int) p->d_name.len, p->d_name.name); | 204 | p, (int) p->d_name.len, p->d_name.name); |
172 | 205 | ||
173 | p = dget_dlock(p); | ||
174 | spin_unlock(&p->d_lock); | ||
175 | spin_unlock(&dcache_lock); | ||
176 | |||
177 | /* | 206 | /* |
178 | * Is someone visiting anywhere in the subtree ? | 207 | * Is someone visiting anywhere in the subtree ? |
179 | * If there's no mount we need to check the usage | 208 | * If there's no mount we need to check the usage |
@@ -208,10 +237,7 @@ static int autofs4_tree_busy(struct vfsmount *mnt, | |||
208 | return 1; | 237 | return 1; |
209 | } | 238 | } |
210 | } | 239 | } |
211 | dput(p); | ||
212 | spin_lock(&dcache_lock); | ||
213 | } | 240 | } |
214 | spin_unlock(&dcache_lock); | ||
215 | 241 | ||
216 | /* Timeout of a tree mount is ultimately determined by its top dentry */ | 242 | /* Timeout of a tree mount is ultimately determined by its top dentry */ |
217 | if (!autofs4_can_expire(top, timeout, do_now)) | 243 | if (!autofs4_can_expire(top, timeout, do_now)) |
@@ -230,36 +256,21 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, | |||
230 | DPRINTK("parent %p %.*s", | 256 | DPRINTK("parent %p %.*s", |
231 | parent, (int)parent->d_name.len, parent->d_name.name); | 257 | parent, (int)parent->d_name.len, parent->d_name.name); |
232 | 258 | ||
233 | spin_lock(&dcache_lock); | 259 | p = NULL; |
234 | for (p = parent; p; p = next_dentry(p, parent)) { | 260 | while ((p = get_next_positive_dentry(p, parent))) { |
235 | spin_lock(&p->d_lock); | ||
236 | /* Negative dentry - give up */ | ||
237 | if (!simple_positive(p)) { | ||
238 | spin_unlock(&p->d_lock); | ||
239 | continue; | ||
240 | } | ||
241 | |||
242 | DPRINTK("dentry %p %.*s", | 261 | DPRINTK("dentry %p %.*s", |
243 | p, (int) p->d_name.len, p->d_name.name); | 262 | p, (int) p->d_name.len, p->d_name.name); |
244 | 263 | ||
245 | p = dget_dlock(p); | ||
246 | spin_unlock(&p->d_lock); | ||
247 | spin_unlock(&dcache_lock); | ||
248 | |||
249 | if (d_mountpoint(p)) { | 264 | if (d_mountpoint(p)) { |
250 | /* Can we umount this guy */ | 265 | /* Can we umount this guy */ |
251 | if (autofs4_mount_busy(mnt, p)) | 266 | if (autofs4_mount_busy(mnt, p)) |
252 | goto cont; | 267 | continue; |
253 | 268 | ||
254 | /* Can we expire this guy */ | 269 | /* Can we expire this guy */ |
255 | if (autofs4_can_expire(p, timeout, do_now)) | 270 | if (autofs4_can_expire(p, timeout, do_now)) |
256 | return p; | 271 | return p; |
257 | } | 272 | } |
258 | cont: | ||
259 | dput(p); | ||
260 | spin_lock(&dcache_lock); | ||
261 | } | 273 | } |
262 | spin_unlock(&dcache_lock); | ||
263 | return NULL; | 274 | return NULL; |
264 | } | 275 | } |
265 | 276 | ||
@@ -310,8 +321,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
310 | { | 321 | { |
311 | unsigned long timeout; | 322 | unsigned long timeout; |
312 | struct dentry *root = sb->s_root; | 323 | struct dentry *root = sb->s_root; |
324 | struct dentry *dentry; | ||
313 | struct dentry *expired = NULL; | 325 | struct dentry *expired = NULL; |
314 | struct list_head *next; | ||
315 | int do_now = how & AUTOFS_EXP_IMMEDIATE; | 326 | int do_now = how & AUTOFS_EXP_IMMEDIATE; |
316 | int exp_leaves = how & AUTOFS_EXP_LEAVES; | 327 | int exp_leaves = how & AUTOFS_EXP_LEAVES; |
317 | struct autofs_info *ino; | 328 | struct autofs_info *ino; |
@@ -323,26 +334,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
323 | now = jiffies; | 334 | now = jiffies; |
324 | timeout = sbi->exp_timeout; | 335 | timeout = sbi->exp_timeout; |
325 | 336 | ||
326 | spin_lock(&dcache_lock); | 337 | dentry = NULL; |
327 | next = root->d_subdirs.next; | 338 | while ((dentry = get_next_positive_dentry(dentry, root))) { |
328 | |||
329 | /* On exit from the loop expire is set to a dgot dentry | ||
330 | * to expire or it's NULL */ | ||
331 | while ( next != &root->d_subdirs ) { | ||
332 | struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); | ||
333 | |||
334 | /* Negative dentry - give up */ | ||
335 | spin_lock(&dentry->d_lock); | ||
336 | if (!simple_positive(dentry)) { | ||
337 | next = next->next; | ||
338 | spin_unlock(&dentry->d_lock); | ||
339 | continue; | ||
340 | } | ||
341 | |||
342 | dentry = dget_dlock(dentry); | ||
343 | spin_unlock(&dentry->d_lock); | ||
344 | spin_unlock(&dcache_lock); | ||
345 | |||
346 | spin_lock(&sbi->fs_lock); | 339 | spin_lock(&sbi->fs_lock); |
347 | ino = autofs4_dentry_ino(dentry); | 340 | ino = autofs4_dentry_ino(dentry); |
348 | 341 | ||
@@ -405,11 +398,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
405 | } | 398 | } |
406 | next: | 399 | next: |
407 | spin_unlock(&sbi->fs_lock); | 400 | spin_unlock(&sbi->fs_lock); |
408 | dput(dentry); | ||
409 | spin_lock(&dcache_lock); | ||
410 | next = next->next; | ||
411 | } | 401 | } |
412 | spin_unlock(&dcache_lock); | ||
413 | return NULL; | 402 | return NULL; |
414 | 403 | ||
415 | found: | 404 | found: |
@@ -420,7 +409,11 @@ found: | |||
420 | init_completion(&ino->expire_complete); | 409 | init_completion(&ino->expire_complete); |
421 | spin_unlock(&sbi->fs_lock); | 410 | spin_unlock(&sbi->fs_lock); |
422 | spin_lock(&dcache_lock); | 411 | spin_lock(&dcache_lock); |
412 | spin_lock(&expired->d_parent->d_lock); | ||
413 | spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED); | ||
423 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); | 414 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); |
415 | spin_unlock(&expired->d_lock); | ||
416 | spin_unlock(&expired->d_parent->d_lock); | ||
424 | spin_unlock(&dcache_lock); | 417 | spin_unlock(&dcache_lock); |
425 | return expired; | 418 | return expired; |
426 | } | 419 | } |
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 7922509b5b2b..7a9ed6b88291 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
@@ -143,10 +143,13 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) | |||
143 | * it. | 143 | * it. |
144 | */ | 144 | */ |
145 | spin_lock(&dcache_lock); | 145 | spin_lock(&dcache_lock); |
146 | spin_lock(&dentry->d_lock); | ||
146 | if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { | 147 | if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { |
148 | spin_unlock(&dentry->d_lock); | ||
147 | spin_unlock(&dcache_lock); | 149 | spin_unlock(&dcache_lock); |
148 | return -ENOENT; | 150 | return -ENOENT; |
149 | } | 151 | } |
152 | spin_unlock(&dentry->d_lock); | ||
150 | spin_unlock(&dcache_lock); | 153 | spin_unlock(&dcache_lock); |
151 | 154 | ||
152 | out: | 155 | out: |
@@ -253,7 +256,9 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
253 | lookup_type = autofs4_need_mount(nd->flags); | 256 | lookup_type = autofs4_need_mount(nd->flags); |
254 | spin_lock(&sbi->fs_lock); | 257 | spin_lock(&sbi->fs_lock); |
255 | spin_lock(&dcache_lock); | 258 | spin_lock(&dcache_lock); |
259 | spin_lock(&dentry->d_lock); | ||
256 | if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) { | 260 | if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) { |
261 | spin_unlock(&dentry->d_lock); | ||
257 | spin_unlock(&dcache_lock); | 262 | spin_unlock(&dcache_lock); |
258 | spin_unlock(&sbi->fs_lock); | 263 | spin_unlock(&sbi->fs_lock); |
259 | goto follow; | 264 | goto follow; |
@@ -266,6 +271,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
266 | */ | 271 | */ |
267 | if (ino->flags & AUTOFS_INF_PENDING || | 272 | if (ino->flags & AUTOFS_INF_PENDING || |
268 | (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) { | 273 | (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) { |
274 | spin_unlock(&dentry->d_lock); | ||
269 | spin_unlock(&dcache_lock); | 275 | spin_unlock(&dcache_lock); |
270 | spin_unlock(&sbi->fs_lock); | 276 | spin_unlock(&sbi->fs_lock); |
271 | 277 | ||
@@ -275,6 +281,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
275 | 281 | ||
276 | goto follow; | 282 | goto follow; |
277 | } | 283 | } |
284 | spin_unlock(&dentry->d_lock); | ||
278 | spin_unlock(&dcache_lock); | 285 | spin_unlock(&dcache_lock); |
279 | spin_unlock(&sbi->fs_lock); | 286 | spin_unlock(&sbi->fs_lock); |
280 | follow: | 287 | follow: |
@@ -347,10 +354,12 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
347 | 354 | ||
348 | /* Check for a non-mountpoint directory with no contents */ | 355 | /* Check for a non-mountpoint directory with no contents */ |
349 | spin_lock(&dcache_lock); | 356 | spin_lock(&dcache_lock); |
357 | spin_lock(&dentry->d_lock); | ||
350 | if (S_ISDIR(dentry->d_inode->i_mode) && | 358 | if (S_ISDIR(dentry->d_inode->i_mode) && |
351 | !d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { | 359 | !d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { |
352 | DPRINTK("dentry=%p %.*s, emptydir", | 360 | DPRINTK("dentry=%p %.*s, emptydir", |
353 | dentry, dentry->d_name.len, dentry->d_name.name); | 361 | dentry, dentry->d_name.len, dentry->d_name.name); |
362 | spin_unlock(&dentry->d_lock); | ||
354 | spin_unlock(&dcache_lock); | 363 | spin_unlock(&dcache_lock); |
355 | 364 | ||
356 | /* The daemon never causes a mount to trigger */ | 365 | /* The daemon never causes a mount to trigger */ |
@@ -367,6 +376,7 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
367 | 376 | ||
368 | return status; | 377 | return status; |
369 | } | 378 | } |
379 | spin_unlock(&dentry->d_lock); | ||
370 | spin_unlock(&dcache_lock); | 380 | spin_unlock(&dcache_lock); |
371 | 381 | ||
372 | return 1; | 382 | return 1; |
@@ -776,12 +786,16 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) | |||
776 | return -EACCES; | 786 | return -EACCES; |
777 | 787 | ||
778 | spin_lock(&dcache_lock); | 788 | spin_lock(&dcache_lock); |
789 | spin_lock(&sbi->lookup_lock); | ||
790 | spin_lock(&dentry->d_lock); | ||
779 | if (!list_empty(&dentry->d_subdirs)) { | 791 | if (!list_empty(&dentry->d_subdirs)) { |
792 | spin_unlock(&dentry->d_lock); | ||
793 | spin_unlock(&sbi->lookup_lock); | ||
780 | spin_unlock(&dcache_lock); | 794 | spin_unlock(&dcache_lock); |
781 | return -ENOTEMPTY; | 795 | return -ENOTEMPTY; |
782 | } | 796 | } |
783 | autofs4_add_expiring(dentry); | 797 | __autofs4_add_expiring(dentry); |
784 | spin_lock(&dentry->d_lock); | 798 | spin_unlock(&sbi->lookup_lock); |
785 | __d_drop(dentry); | 799 | __d_drop(dentry); |
786 | spin_unlock(&dentry->d_lock); | 800 | spin_unlock(&dentry->d_lock); |
787 | spin_unlock(&dcache_lock); | 801 | spin_unlock(&dcache_lock); |
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 571f270dca0f..2c924e8d85fe 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c | |||
@@ -113,6 +113,7 @@ static int __dcache_readdir(struct file *filp, | |||
113 | last); | 113 | last); |
114 | 114 | ||
115 | spin_lock(&dcache_lock); | 115 | spin_lock(&dcache_lock); |
116 | spin_lock(&parent->d_lock); | ||
116 | 117 | ||
117 | /* start at beginning? */ | 118 | /* start at beginning? */ |
118 | if (filp->f_pos == 2 || last == NULL || | 119 | if (filp->f_pos == 2 || last == NULL || |
@@ -136,7 +137,7 @@ more: | |||
136 | fi->at_end = 1; | 137 | fi->at_end = 1; |
137 | goto out_unlock; | 138 | goto out_unlock; |
138 | } | 139 | } |
139 | spin_lock(&dentry->d_lock); | 140 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); |
140 | if (!d_unhashed(dentry) && dentry->d_inode && | 141 | if (!d_unhashed(dentry) && dentry->d_inode && |
141 | ceph_snap(dentry->d_inode) != CEPH_SNAPDIR && | 142 | ceph_snap(dentry->d_inode) != CEPH_SNAPDIR && |
142 | ceph_ino(dentry->d_inode) != CEPH_INO_CEPH && | 143 | ceph_ino(dentry->d_inode) != CEPH_INO_CEPH && |
@@ -154,6 +155,7 @@ more: | |||
154 | 155 | ||
155 | dget_dlock(dentry); | 156 | dget_dlock(dentry); |
156 | spin_unlock(&dentry->d_lock); | 157 | spin_unlock(&dentry->d_lock); |
158 | spin_unlock(&parent->d_lock); | ||
157 | spin_unlock(&dcache_lock); | 159 | spin_unlock(&dcache_lock); |
158 | 160 | ||
159 | dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos, | 161 | dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos, |
@@ -188,10 +190,12 @@ more: | |||
188 | } | 190 | } |
189 | 191 | ||
190 | spin_lock(&dcache_lock); | 192 | spin_lock(&dcache_lock); |
193 | spin_lock(&parent->d_lock); | ||
191 | p = p->prev; /* advance to next dentry */ | 194 | p = p->prev; /* advance to next dentry */ |
192 | goto more; | 195 | goto more; |
193 | 196 | ||
194 | out_unlock: | 197 | out_unlock: |
198 | spin_unlock(&parent->d_lock); | ||
195 | spin_unlock(&dcache_lock); | 199 | spin_unlock(&dcache_lock); |
196 | out: | 200 | out: |
197 | if (last) | 201 | if (last) |
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index bb68c799074d..2c6944473366 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c | |||
@@ -842,11 +842,13 @@ static void ceph_set_dentry_offset(struct dentry *dn) | |||
842 | spin_unlock(&inode->i_lock); | 842 | spin_unlock(&inode->i_lock); |
843 | 843 | ||
844 | spin_lock(&dcache_lock); | 844 | spin_lock(&dcache_lock); |
845 | spin_lock(&dn->d_lock); | 845 | spin_lock(&dir->d_lock); |
846 | spin_lock_nested(&dn->d_lock, DENTRY_D_LOCK_NESTED); | ||
846 | list_move(&dn->d_u.d_child, &dir->d_subdirs); | 847 | list_move(&dn->d_u.d_child, &dir->d_subdirs); |
847 | dout("set_dentry_offset %p %lld (%p %p)\n", dn, di->offset, | 848 | dout("set_dentry_offset %p %lld (%p %p)\n", dn, di->offset, |
848 | dn->d_u.d_child.prev, dn->d_u.d_child.next); | 849 | dn->d_u.d_child.prev, dn->d_u.d_child.next); |
849 | spin_unlock(&dn->d_lock); | 850 | spin_unlock(&dn->d_lock); |
851 | spin_unlock(&dir->d_lock); | ||
850 | spin_unlock(&dcache_lock); | 852 | spin_unlock(&dcache_lock); |
851 | } | 853 | } |
852 | 854 | ||
@@ -1232,9 +1234,11 @@ retry_lookup: | |||
1232 | } else { | 1234 | } else { |
1233 | /* reorder parent's d_subdirs */ | 1235 | /* reorder parent's d_subdirs */ |
1234 | spin_lock(&dcache_lock); | 1236 | spin_lock(&dcache_lock); |
1235 | spin_lock(&dn->d_lock); | 1237 | spin_lock(&parent->d_lock); |
1238 | spin_lock_nested(&dn->d_lock, DENTRY_D_LOCK_NESTED); | ||
1236 | list_move(&dn->d_u.d_child, &parent->d_subdirs); | 1239 | list_move(&dn->d_u.d_child, &parent->d_subdirs); |
1237 | spin_unlock(&dn->d_lock); | 1240 | spin_unlock(&dn->d_lock); |
1241 | spin_unlock(&parent->d_lock); | ||
1238 | spin_unlock(&dcache_lock); | 1242 | spin_unlock(&dcache_lock); |
1239 | } | 1243 | } |
1240 | 1244 | ||
diff --git a/fs/coda/cache.c b/fs/coda/cache.c index 9060f08e70cf..859393fca2b7 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c | |||
@@ -94,6 +94,7 @@ static void coda_flag_children(struct dentry *parent, int flag) | |||
94 | struct dentry *de; | 94 | struct dentry *de; |
95 | 95 | ||
96 | spin_lock(&dcache_lock); | 96 | spin_lock(&dcache_lock); |
97 | spin_lock(&parent->d_lock); | ||
97 | list_for_each(child, &parent->d_subdirs) | 98 | list_for_each(child, &parent->d_subdirs) |
98 | { | 99 | { |
99 | de = list_entry(child, struct dentry, d_u.d_child); | 100 | de = list_entry(child, struct dentry, d_u.d_child); |
@@ -102,6 +103,7 @@ static void coda_flag_children(struct dentry *parent, int flag) | |||
102 | continue; | 103 | continue; |
103 | coda_flag_inode(de->d_inode, flag); | 104 | coda_flag_inode(de->d_inode, flag); |
104 | } | 105 | } |
106 | spin_unlock(&parent->d_lock); | ||
105 | spin_unlock(&dcache_lock); | 107 | spin_unlock(&dcache_lock); |
106 | return; | 108 | return; |
107 | } | 109 | } |
diff --git a/fs/dcache.c b/fs/dcache.c index ee127f9ab274..a661247a20d5 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -47,6 +47,8 @@ | |||
47 | * - d_lru | 47 | * - d_lru |
48 | * - d_count | 48 | * - d_count |
49 | * - d_unhashed() | 49 | * - d_unhashed() |
50 | * - d_parent and d_subdirs | ||
51 | * - childrens' d_child and d_parent | ||
50 | * | 52 | * |
51 | * Ordering: | 53 | * Ordering: |
52 | * dcache_lock | 54 | * dcache_lock |
@@ -223,24 +225,22 @@ static void dentry_lru_move_tail(struct dentry *dentry) | |||
223 | * | 225 | * |
224 | * If this is the root of the dentry tree, return NULL. | 226 | * If this is the root of the dentry tree, return NULL. |
225 | * | 227 | * |
226 | * dcache_lock and d_lock must be held by caller, are dropped by d_kill. | 228 | * dcache_lock and d_lock and d_parent->d_lock must be held by caller, and |
229 | * are dropped by d_kill. | ||
227 | */ | 230 | */ |
228 | static struct dentry *d_kill(struct dentry *dentry) | 231 | static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent) |
229 | __releases(dentry->d_lock) | 232 | __releases(dentry->d_lock) |
233 | __releases(parent->d_lock) | ||
230 | __releases(dcache_lock) | 234 | __releases(dcache_lock) |
231 | { | 235 | { |
232 | struct dentry *parent; | ||
233 | |||
234 | list_del(&dentry->d_u.d_child); | 236 | list_del(&dentry->d_u.d_child); |
237 | if (parent) | ||
238 | spin_unlock(&parent->d_lock); | ||
235 | dentry_iput(dentry); | 239 | dentry_iput(dentry); |
236 | /* | 240 | /* |
237 | * dentry_iput drops the locks, at which point nobody (except | 241 | * dentry_iput drops the locks, at which point nobody (except |
238 | * transient RCU lookups) can reach this dentry. | 242 | * transient RCU lookups) can reach this dentry. |
239 | */ | 243 | */ |
240 | if (IS_ROOT(dentry)) | ||
241 | parent = NULL; | ||
242 | else | ||
243 | parent = dentry->d_parent; | ||
244 | d_free(dentry); | 244 | d_free(dentry); |
245 | return parent; | 245 | return parent; |
246 | } | 246 | } |
@@ -312,6 +312,7 @@ EXPORT_SYMBOL(d_drop); | |||
312 | 312 | ||
313 | void dput(struct dentry *dentry) | 313 | void dput(struct dentry *dentry) |
314 | { | 314 | { |
315 | struct dentry *parent; | ||
315 | if (!dentry) | 316 | if (!dentry) |
316 | return; | 317 | return; |
317 | 318 | ||
@@ -319,6 +320,10 @@ repeat: | |||
319 | if (dentry->d_count == 1) | 320 | if (dentry->d_count == 1) |
320 | might_sleep(); | 321 | might_sleep(); |
321 | spin_lock(&dentry->d_lock); | 322 | spin_lock(&dentry->d_lock); |
323 | if (IS_ROOT(dentry)) | ||
324 | parent = NULL; | ||
325 | else | ||
326 | parent = dentry->d_parent; | ||
322 | if (dentry->d_count == 1) { | 327 | if (dentry->d_count == 1) { |
323 | if (!spin_trylock(&dcache_lock)) { | 328 | if (!spin_trylock(&dcache_lock)) { |
324 | /* | 329 | /* |
@@ -330,10 +335,17 @@ repeat: | |||
330 | spin_unlock(&dentry->d_lock); | 335 | spin_unlock(&dentry->d_lock); |
331 | goto repeat; | 336 | goto repeat; |
332 | } | 337 | } |
338 | if (parent && !spin_trylock(&parent->d_lock)) { | ||
339 | spin_unlock(&dentry->d_lock); | ||
340 | spin_unlock(&dcache_lock); | ||
341 | goto repeat; | ||
342 | } | ||
333 | } | 343 | } |
334 | dentry->d_count--; | 344 | dentry->d_count--; |
335 | if (dentry->d_count) { | 345 | if (dentry->d_count) { |
336 | spin_unlock(&dentry->d_lock); | 346 | spin_unlock(&dentry->d_lock); |
347 | if (parent) | ||
348 | spin_unlock(&parent->d_lock); | ||
337 | spin_unlock(&dcache_lock); | 349 | spin_unlock(&dcache_lock); |
338 | return; | 350 | return; |
339 | } | 351 | } |
@@ -355,6 +367,8 @@ repeat: | |||
355 | dentry_lru_add(dentry); | 367 | dentry_lru_add(dentry); |
356 | 368 | ||
357 | spin_unlock(&dentry->d_lock); | 369 | spin_unlock(&dentry->d_lock); |
370 | if (parent) | ||
371 | spin_unlock(&parent->d_lock); | ||
358 | spin_unlock(&dcache_lock); | 372 | spin_unlock(&dcache_lock); |
359 | return; | 373 | return; |
360 | 374 | ||
@@ -363,7 +377,7 @@ unhash_it: | |||
363 | kill_it: | 377 | kill_it: |
364 | /* if dentry was on the d_lru list delete it from there */ | 378 | /* if dentry was on the d_lru list delete it from there */ |
365 | dentry_lru_del(dentry); | 379 | dentry_lru_del(dentry); |
366 | dentry = d_kill(dentry); | 380 | dentry = d_kill(dentry, parent); |
367 | if (dentry) | 381 | if (dentry) |
368 | goto repeat; | 382 | goto repeat; |
369 | } | 383 | } |
@@ -584,12 +598,13 @@ EXPORT_SYMBOL(d_prune_aliases); | |||
584 | * quadratic behavior of shrink_dcache_parent(), but is also expected | 598 | * quadratic behavior of shrink_dcache_parent(), but is also expected |
585 | * to be beneficial in reducing dentry cache fragmentation. | 599 | * to be beneficial in reducing dentry cache fragmentation. |
586 | */ | 600 | */ |
587 | static void prune_one_dentry(struct dentry * dentry) | 601 | static void prune_one_dentry(struct dentry *dentry, struct dentry *parent) |
588 | __releases(dentry->d_lock) | 602 | __releases(dentry->d_lock) |
603 | __releases(parent->d_lock) | ||
589 | __releases(dcache_lock) | 604 | __releases(dcache_lock) |
590 | { | 605 | { |
591 | __d_drop(dentry); | 606 | __d_drop(dentry); |
592 | dentry = d_kill(dentry); | 607 | dentry = d_kill(dentry, parent); |
593 | 608 | ||
594 | /* | 609 | /* |
595 | * Prune ancestors. Locking is simpler than in dput(), | 610 | * Prune ancestors. Locking is simpler than in dput(), |
@@ -597,9 +612,20 @@ static void prune_one_dentry(struct dentry * dentry) | |||
597 | */ | 612 | */ |
598 | while (dentry) { | 613 | while (dentry) { |
599 | spin_lock(&dcache_lock); | 614 | spin_lock(&dcache_lock); |
615 | again: | ||
600 | spin_lock(&dentry->d_lock); | 616 | spin_lock(&dentry->d_lock); |
617 | if (IS_ROOT(dentry)) | ||
618 | parent = NULL; | ||
619 | else | ||
620 | parent = dentry->d_parent; | ||
621 | if (parent && !spin_trylock(&parent->d_lock)) { | ||
622 | spin_unlock(&dentry->d_lock); | ||
623 | goto again; | ||
624 | } | ||
601 | dentry->d_count--; | 625 | dentry->d_count--; |
602 | if (dentry->d_count) { | 626 | if (dentry->d_count) { |
627 | if (parent) | ||
628 | spin_unlock(&parent->d_lock); | ||
603 | spin_unlock(&dentry->d_lock); | 629 | spin_unlock(&dentry->d_lock); |
604 | spin_unlock(&dcache_lock); | 630 | spin_unlock(&dcache_lock); |
605 | return; | 631 | return; |
@@ -607,7 +633,7 @@ static void prune_one_dentry(struct dentry * dentry) | |||
607 | 633 | ||
608 | dentry_lru_del(dentry); | 634 | dentry_lru_del(dentry); |
609 | __d_drop(dentry); | 635 | __d_drop(dentry); |
610 | dentry = d_kill(dentry); | 636 | dentry = d_kill(dentry, parent); |
611 | } | 637 | } |
612 | } | 638 | } |
613 | 639 | ||
@@ -616,29 +642,40 @@ static void shrink_dentry_list(struct list_head *list) | |||
616 | struct dentry *dentry; | 642 | struct dentry *dentry; |
617 | 643 | ||
618 | while (!list_empty(list)) { | 644 | while (!list_empty(list)) { |
645 | struct dentry *parent; | ||
646 | |||
619 | dentry = list_entry(list->prev, struct dentry, d_lru); | 647 | dentry = list_entry(list->prev, struct dentry, d_lru); |
620 | 648 | ||
621 | if (!spin_trylock(&dentry->d_lock)) { | 649 | if (!spin_trylock(&dentry->d_lock)) { |
650 | relock: | ||
622 | spin_unlock(&dcache_lru_lock); | 651 | spin_unlock(&dcache_lru_lock); |
623 | cpu_relax(); | 652 | cpu_relax(); |
624 | spin_lock(&dcache_lru_lock); | 653 | spin_lock(&dcache_lru_lock); |
625 | continue; | 654 | continue; |
626 | } | 655 | } |
627 | 656 | ||
628 | __dentry_lru_del(dentry); | ||
629 | |||
630 | /* | 657 | /* |
631 | * We found an inuse dentry which was not removed from | 658 | * We found an inuse dentry which was not removed from |
632 | * the LRU because of laziness during lookup. Do not free | 659 | * the LRU because of laziness during lookup. Do not free |
633 | * it - just keep it off the LRU list. | 660 | * it - just keep it off the LRU list. |
634 | */ | 661 | */ |
635 | if (dentry->d_count) { | 662 | if (dentry->d_count) { |
663 | __dentry_lru_del(dentry); | ||
636 | spin_unlock(&dentry->d_lock); | 664 | spin_unlock(&dentry->d_lock); |
637 | continue; | 665 | continue; |
638 | } | 666 | } |
667 | if (IS_ROOT(dentry)) | ||
668 | parent = NULL; | ||
669 | else | ||
670 | parent = dentry->d_parent; | ||
671 | if (parent && !spin_trylock(&parent->d_lock)) { | ||
672 | spin_unlock(&dentry->d_lock); | ||
673 | goto relock; | ||
674 | } | ||
675 | __dentry_lru_del(dentry); | ||
639 | spin_unlock(&dcache_lru_lock); | 676 | spin_unlock(&dcache_lru_lock); |
640 | 677 | ||
641 | prune_one_dentry(dentry); | 678 | prune_one_dentry(dentry, parent); |
642 | /* dcache_lock and dentry->d_lock dropped */ | 679 | /* dcache_lock and dentry->d_lock dropped */ |
643 | spin_lock(&dcache_lock); | 680 | spin_lock(&dcache_lock); |
644 | spin_lock(&dcache_lru_lock); | 681 | spin_lock(&dcache_lru_lock); |
@@ -833,14 +870,16 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) | |||
833 | /* this is a branch with children - detach all of them | 870 | /* this is a branch with children - detach all of them |
834 | * from the system in one go */ | 871 | * from the system in one go */ |
835 | spin_lock(&dcache_lock); | 872 | spin_lock(&dcache_lock); |
873 | spin_lock(&dentry->d_lock); | ||
836 | list_for_each_entry(loop, &dentry->d_subdirs, | 874 | list_for_each_entry(loop, &dentry->d_subdirs, |
837 | d_u.d_child) { | 875 | d_u.d_child) { |
838 | spin_lock(&loop->d_lock); | 876 | spin_lock_nested(&loop->d_lock, |
877 | DENTRY_D_LOCK_NESTED); | ||
839 | dentry_lru_del(loop); | 878 | dentry_lru_del(loop); |
840 | __d_drop(loop); | 879 | __d_drop(loop); |
841 | spin_unlock(&loop->d_lock); | 880 | spin_unlock(&loop->d_lock); |
842 | cond_resched_lock(&dcache_lock); | ||
843 | } | 881 | } |
882 | spin_unlock(&dentry->d_lock); | ||
844 | spin_unlock(&dcache_lock); | 883 | spin_unlock(&dcache_lock); |
845 | 884 | ||
846 | /* move to the first child */ | 885 | /* move to the first child */ |
@@ -868,16 +907,17 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) | |||
868 | BUG(); | 907 | BUG(); |
869 | } | 908 | } |
870 | 909 | ||
871 | if (IS_ROOT(dentry)) | 910 | if (IS_ROOT(dentry)) { |
872 | parent = NULL; | 911 | parent = NULL; |
873 | else { | 912 | list_del(&dentry->d_u.d_child); |
913 | } else { | ||
874 | parent = dentry->d_parent; | 914 | parent = dentry->d_parent; |
875 | spin_lock(&parent->d_lock); | 915 | spin_lock(&parent->d_lock); |
876 | parent->d_count--; | 916 | parent->d_count--; |
917 | list_del(&dentry->d_u.d_child); | ||
877 | spin_unlock(&parent->d_lock); | 918 | spin_unlock(&parent->d_lock); |
878 | } | 919 | } |
879 | 920 | ||
880 | list_del(&dentry->d_u.d_child); | ||
881 | detached++; | 921 | detached++; |
882 | 922 | ||
883 | inode = dentry->d_inode; | 923 | inode = dentry->d_inode; |
@@ -958,6 +998,7 @@ int have_submounts(struct dentry *parent) | |||
958 | spin_lock(&dcache_lock); | 998 | spin_lock(&dcache_lock); |
959 | if (d_mountpoint(parent)) | 999 | if (d_mountpoint(parent)) |
960 | goto positive; | 1000 | goto positive; |
1001 | spin_lock(&this_parent->d_lock); | ||
961 | repeat: | 1002 | repeat: |
962 | next = this_parent->d_subdirs.next; | 1003 | next = this_parent->d_subdirs.next; |
963 | resume: | 1004 | resume: |
@@ -965,22 +1006,34 @@ resume: | |||
965 | struct list_head *tmp = next; | 1006 | struct list_head *tmp = next; |
966 | struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); | 1007 | struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); |
967 | next = tmp->next; | 1008 | next = tmp->next; |
1009 | |||
1010 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); | ||
968 | /* Have we found a mount point ? */ | 1011 | /* Have we found a mount point ? */ |
969 | if (d_mountpoint(dentry)) | 1012 | if (d_mountpoint(dentry)) { |
1013 | spin_unlock(&dentry->d_lock); | ||
1014 | spin_unlock(&this_parent->d_lock); | ||
970 | goto positive; | 1015 | goto positive; |
1016 | } | ||
971 | if (!list_empty(&dentry->d_subdirs)) { | 1017 | if (!list_empty(&dentry->d_subdirs)) { |
1018 | spin_unlock(&this_parent->d_lock); | ||
1019 | spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_); | ||
972 | this_parent = dentry; | 1020 | this_parent = dentry; |
1021 | spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_); | ||
973 | goto repeat; | 1022 | goto repeat; |
974 | } | 1023 | } |
1024 | spin_unlock(&dentry->d_lock); | ||
975 | } | 1025 | } |
976 | /* | 1026 | /* |
977 | * All done at this level ... ascend and resume the search. | 1027 | * All done at this level ... ascend and resume the search. |
978 | */ | 1028 | */ |
979 | if (this_parent != parent) { | 1029 | if (this_parent != parent) { |
980 | next = this_parent->d_u.d_child.next; | 1030 | next = this_parent->d_u.d_child.next; |
1031 | spin_unlock(&this_parent->d_lock); | ||
981 | this_parent = this_parent->d_parent; | 1032 | this_parent = this_parent->d_parent; |
1033 | spin_lock(&this_parent->d_lock); | ||
982 | goto resume; | 1034 | goto resume; |
983 | } | 1035 | } |
1036 | spin_unlock(&this_parent->d_lock); | ||
984 | spin_unlock(&dcache_lock); | 1037 | spin_unlock(&dcache_lock); |
985 | return 0; /* No mount points found in tree */ | 1038 | return 0; /* No mount points found in tree */ |
986 | positive: | 1039 | positive: |
@@ -1010,6 +1063,7 @@ static int select_parent(struct dentry * parent) | |||
1010 | int found = 0; | 1063 | int found = 0; |
1011 | 1064 | ||
1012 | spin_lock(&dcache_lock); | 1065 | spin_lock(&dcache_lock); |
1066 | spin_lock(&this_parent->d_lock); | ||
1013 | repeat: | 1067 | repeat: |
1014 | next = this_parent->d_subdirs.next; | 1068 | next = this_parent->d_subdirs.next; |
1015 | resume: | 1069 | resume: |
@@ -1017,8 +1071,9 @@ resume: | |||
1017 | struct list_head *tmp = next; | 1071 | struct list_head *tmp = next; |
1018 | struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); | 1072 | struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); |
1019 | next = tmp->next; | 1073 | next = tmp->next; |
1074 | BUG_ON(this_parent == dentry); | ||
1020 | 1075 | ||
1021 | spin_lock(&dentry->d_lock); | 1076 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); |
1022 | 1077 | ||
1023 | /* | 1078 | /* |
1024 | * move only zero ref count dentries to the end | 1079 | * move only zero ref count dentries to the end |
@@ -1031,33 +1086,44 @@ resume: | |||
1031 | dentry_lru_del(dentry); | 1086 | dentry_lru_del(dentry); |
1032 | } | 1087 | } |
1033 | 1088 | ||
1034 | spin_unlock(&dentry->d_lock); | ||
1035 | |||
1036 | /* | 1089 | /* |
1037 | * We can return to the caller if we have found some (this | 1090 | * We can return to the caller if we have found some (this |
1038 | * ensures forward progress). We'll be coming back to find | 1091 | * ensures forward progress). We'll be coming back to find |
1039 | * the rest. | 1092 | * the rest. |
1040 | */ | 1093 | */ |
1041 | if (found && need_resched()) | 1094 | if (found && need_resched()) { |
1095 | spin_unlock(&dentry->d_lock); | ||
1042 | goto out; | 1096 | goto out; |
1097 | } | ||
1043 | 1098 | ||
1044 | /* | 1099 | /* |
1045 | * Descend a level if the d_subdirs list is non-empty. | 1100 | * Descend a level if the d_subdirs list is non-empty. |
1046 | */ | 1101 | */ |
1047 | if (!list_empty(&dentry->d_subdirs)) { | 1102 | if (!list_empty(&dentry->d_subdirs)) { |
1103 | spin_unlock(&this_parent->d_lock); | ||
1104 | spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_); | ||
1048 | this_parent = dentry; | 1105 | this_parent = dentry; |
1106 | spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_); | ||
1049 | goto repeat; | 1107 | goto repeat; |
1050 | } | 1108 | } |
1109 | |||
1110 | spin_unlock(&dentry->d_lock); | ||
1051 | } | 1111 | } |
1052 | /* | 1112 | /* |
1053 | * All done at this level ... ascend and resume the search. | 1113 | * All done at this level ... ascend and resume the search. |
1054 | */ | 1114 | */ |
1055 | if (this_parent != parent) { | 1115 | if (this_parent != parent) { |
1116 | struct dentry *tmp; | ||
1056 | next = this_parent->d_u.d_child.next; | 1117 | next = this_parent->d_u.d_child.next; |
1057 | this_parent = this_parent->d_parent; | 1118 | tmp = this_parent->d_parent; |
1119 | spin_unlock(&this_parent->d_lock); | ||
1120 | BUG_ON(tmp == this_parent); | ||
1121 | this_parent = tmp; | ||
1122 | spin_lock(&this_parent->d_lock); | ||
1058 | goto resume; | 1123 | goto resume; |
1059 | } | 1124 | } |
1060 | out: | 1125 | out: |
1126 | spin_unlock(&this_parent->d_lock); | ||
1061 | spin_unlock(&dcache_lock); | 1127 | spin_unlock(&dcache_lock); |
1062 | return found; | 1128 | return found; |
1063 | } | 1129 | } |
@@ -1155,18 +1221,19 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) | |||
1155 | INIT_LIST_HEAD(&dentry->d_lru); | 1221 | INIT_LIST_HEAD(&dentry->d_lru); |
1156 | INIT_LIST_HEAD(&dentry->d_subdirs); | 1222 | INIT_LIST_HEAD(&dentry->d_subdirs); |
1157 | INIT_LIST_HEAD(&dentry->d_alias); | 1223 | INIT_LIST_HEAD(&dentry->d_alias); |
1224 | INIT_LIST_HEAD(&dentry->d_u.d_child); | ||
1158 | 1225 | ||
1159 | if (parent) { | 1226 | if (parent) { |
1160 | dentry->d_parent = dget(parent); | 1227 | spin_lock(&dcache_lock); |
1228 | spin_lock(&parent->d_lock); | ||
1229 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); | ||
1230 | dentry->d_parent = dget_dlock(parent); | ||
1161 | dentry->d_sb = parent->d_sb; | 1231 | dentry->d_sb = parent->d_sb; |
1162 | } else { | ||
1163 | INIT_LIST_HEAD(&dentry->d_u.d_child); | ||
1164 | } | ||
1165 | |||
1166 | spin_lock(&dcache_lock); | ||
1167 | if (parent) | ||
1168 | list_add(&dentry->d_u.d_child, &parent->d_subdirs); | 1232 | list_add(&dentry->d_u.d_child, &parent->d_subdirs); |
1169 | spin_unlock(&dcache_lock); | 1233 | spin_unlock(&dentry->d_lock); |
1234 | spin_unlock(&parent->d_lock); | ||
1235 | spin_unlock(&dcache_lock); | ||
1236 | } | ||
1170 | 1237 | ||
1171 | this_cpu_inc(nr_dentry); | 1238 | this_cpu_inc(nr_dentry); |
1172 | 1239 | ||
@@ -1684,13 +1751,18 @@ int d_validate(struct dentry *dentry, struct dentry *dparent) | |||
1684 | struct dentry *child; | 1751 | struct dentry *child; |
1685 | 1752 | ||
1686 | spin_lock(&dcache_lock); | 1753 | spin_lock(&dcache_lock); |
1754 | spin_lock(&dparent->d_lock); | ||
1687 | list_for_each_entry(child, &dparent->d_subdirs, d_u.d_child) { | 1755 | list_for_each_entry(child, &dparent->d_subdirs, d_u.d_child) { |
1688 | if (dentry == child) { | 1756 | if (dentry == child) { |
1689 | __dget_locked(dentry); | 1757 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); |
1758 | __dget_locked_dlock(dentry); | ||
1759 | spin_unlock(&dentry->d_lock); | ||
1760 | spin_unlock(&dparent->d_lock); | ||
1690 | spin_unlock(&dcache_lock); | 1761 | spin_unlock(&dcache_lock); |
1691 | return 1; | 1762 | return 1; |
1692 | } | 1763 | } |
1693 | } | 1764 | } |
1765 | spin_unlock(&dparent->d_lock); | ||
1694 | spin_unlock(&dcache_lock); | 1766 | spin_unlock(&dcache_lock); |
1695 | 1767 | ||
1696 | return 0; | 1768 | return 0; |
@@ -1802,17 +1874,6 @@ void dentry_update_name_case(struct dentry *dentry, struct qstr *name) | |||
1802 | } | 1874 | } |
1803 | EXPORT_SYMBOL(dentry_update_name_case); | 1875 | EXPORT_SYMBOL(dentry_update_name_case); |
1804 | 1876 | ||
1805 | /* | ||
1806 | * When switching names, the actual string doesn't strictly have to | ||
1807 | * be preserved in the target - because we're dropping the target | ||
1808 | * anyway. As such, we can just do a simple memcpy() to copy over | ||
1809 | * the new name before we switch. | ||
1810 | * | ||
1811 | * Note that we have to be a lot more careful about getting the hash | ||
1812 | * switched - we have to switch the hash value properly even if it | ||
1813 | * then no longer matches the actual (corrupted) string of the target. | ||
1814 | * The hash value has to match the hash queue that the dentry is on.. | ||
1815 | */ | ||
1816 | static void switch_names(struct dentry *dentry, struct dentry *target) | 1877 | static void switch_names(struct dentry *dentry, struct dentry *target) |
1817 | { | 1878 | { |
1818 | if (dname_external(target)) { | 1879 | if (dname_external(target)) { |
@@ -1854,18 +1915,53 @@ static void switch_names(struct dentry *dentry, struct dentry *target) | |||
1854 | swap(dentry->d_name.len, target->d_name.len); | 1915 | swap(dentry->d_name.len, target->d_name.len); |
1855 | } | 1916 | } |
1856 | 1917 | ||
1918 | static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target) | ||
1919 | { | ||
1920 | /* | ||
1921 | * XXXX: do we really need to take target->d_lock? | ||
1922 | */ | ||
1923 | if (IS_ROOT(dentry) || dentry->d_parent == target->d_parent) | ||
1924 | spin_lock(&target->d_parent->d_lock); | ||
1925 | else { | ||
1926 | if (d_ancestor(dentry->d_parent, target->d_parent)) { | ||
1927 | spin_lock(&dentry->d_parent->d_lock); | ||
1928 | spin_lock_nested(&target->d_parent->d_lock, | ||
1929 | DENTRY_D_LOCK_NESTED); | ||
1930 | } else { | ||
1931 | spin_lock(&target->d_parent->d_lock); | ||
1932 | spin_lock_nested(&dentry->d_parent->d_lock, | ||
1933 | DENTRY_D_LOCK_NESTED); | ||
1934 | } | ||
1935 | } | ||
1936 | if (target < dentry) { | ||
1937 | spin_lock_nested(&target->d_lock, 2); | ||
1938 | spin_lock_nested(&dentry->d_lock, 3); | ||
1939 | } else { | ||
1940 | spin_lock_nested(&dentry->d_lock, 2); | ||
1941 | spin_lock_nested(&target->d_lock, 3); | ||
1942 | } | ||
1943 | } | ||
1944 | |||
1945 | static void dentry_unlock_parents_for_move(struct dentry *dentry, | ||
1946 | struct dentry *target) | ||
1947 | { | ||
1948 | if (target->d_parent != dentry->d_parent) | ||
1949 | spin_unlock(&dentry->d_parent->d_lock); | ||
1950 | if (target->d_parent != target) | ||
1951 | spin_unlock(&target->d_parent->d_lock); | ||
1952 | } | ||
1953 | |||
1857 | /* | 1954 | /* |
1858 | * We cannibalize "target" when moving dentry on top of it, | 1955 | * When switching names, the actual string doesn't strictly have to |
1859 | * because it's going to be thrown away anyway. We could be more | 1956 | * be preserved in the target - because we're dropping the target |
1860 | * polite about it, though. | 1957 | * anyway. As such, we can just do a simple memcpy() to copy over |
1861 | * | 1958 | * the new name before we switch. |
1862 | * This forceful removal will result in ugly /proc output if | 1959 | * |
1863 | * somebody holds a file open that got deleted due to a rename. | 1960 | * Note that we have to be a lot more careful about getting the hash |
1864 | * We could be nicer about the deleted file, and let it show | 1961 | * switched - we have to switch the hash value properly even if it |
1865 | * up under the name it had before it was deleted rather than | 1962 | * then no longer matches the actual (corrupted) string of the target. |
1866 | * under the original name of the file that was moved on top of it. | 1963 | * The hash value has to match the hash queue that the dentry is on.. |
1867 | */ | 1964 | */ |
1868 | |||
1869 | /* | 1965 | /* |
1870 | * d_move_locked - move a dentry | 1966 | * d_move_locked - move a dentry |
1871 | * @dentry: entry to move | 1967 | * @dentry: entry to move |
@@ -1879,20 +1975,12 @@ static void d_move_locked(struct dentry * dentry, struct dentry * target) | |||
1879 | if (!dentry->d_inode) | 1975 | if (!dentry->d_inode) |
1880 | printk(KERN_WARNING "VFS: moving negative dcache entry\n"); | 1976 | printk(KERN_WARNING "VFS: moving negative dcache entry\n"); |
1881 | 1977 | ||
1978 | BUG_ON(d_ancestor(dentry, target)); | ||
1979 | BUG_ON(d_ancestor(target, dentry)); | ||
1980 | |||
1882 | write_seqlock(&rename_lock); | 1981 | write_seqlock(&rename_lock); |
1883 | /* | 1982 | |
1884 | * XXXX: do we really need to take target->d_lock? | 1983 | dentry_lock_for_move(dentry, target); |
1885 | */ | ||
1886 | if (d_ancestor(dentry, target)) { | ||
1887 | spin_lock(&dentry->d_lock); | ||
1888 | spin_lock_nested(&target->d_lock, DENTRY_D_LOCK_NESTED); | ||
1889 | } else if (d_ancestor(target, dentry) || target < dentry) { | ||
1890 | spin_lock(&target->d_lock); | ||
1891 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); | ||
1892 | } else { | ||
1893 | spin_lock(&dentry->d_lock); | ||
1894 | spin_lock_nested(&target->d_lock, DENTRY_D_LOCK_NESTED); | ||
1895 | } | ||
1896 | 1984 | ||
1897 | /* Move the dentry to the target hash queue, if on different bucket */ | 1985 | /* Move the dentry to the target hash queue, if on different bucket */ |
1898 | spin_lock(&dcache_hash_lock); | 1986 | spin_lock(&dcache_hash_lock); |
@@ -1924,6 +2012,8 @@ static void d_move_locked(struct dentry * dentry, struct dentry * target) | |||
1924 | } | 2012 | } |
1925 | 2013 | ||
1926 | list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs); | 2014 | list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs); |
2015 | |||
2016 | dentry_unlock_parents_for_move(dentry, target); | ||
1927 | spin_unlock(&target->d_lock); | 2017 | spin_unlock(&target->d_lock); |
1928 | fsnotify_d_move(dentry); | 2018 | fsnotify_d_move(dentry); |
1929 | spin_unlock(&dentry->d_lock); | 2019 | spin_unlock(&dentry->d_lock); |
@@ -2013,17 +2103,20 @@ out_err: | |||
2013 | /* | 2103 | /* |
2014 | * Prepare an anonymous dentry for life in the superblock's dentry tree as a | 2104 | * Prepare an anonymous dentry for life in the superblock's dentry tree as a |
2015 | * named dentry in place of the dentry to be replaced. | 2105 | * named dentry in place of the dentry to be replaced. |
2106 | * returns with anon->d_lock held! | ||
2016 | */ | 2107 | */ |
2017 | static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon) | 2108 | static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon) |
2018 | { | 2109 | { |
2019 | struct dentry *dparent, *aparent; | 2110 | struct dentry *dparent, *aparent; |
2020 | 2111 | ||
2021 | switch_names(dentry, anon); | 2112 | dentry_lock_for_move(anon, dentry); |
2022 | swap(dentry->d_name.hash, anon->d_name.hash); | ||
2023 | 2113 | ||
2024 | dparent = dentry->d_parent; | 2114 | dparent = dentry->d_parent; |
2025 | aparent = anon->d_parent; | 2115 | aparent = anon->d_parent; |
2026 | 2116 | ||
2117 | switch_names(dentry, anon); | ||
2118 | swap(dentry->d_name.hash, anon->d_name.hash); | ||
2119 | |||
2027 | dentry->d_parent = (aparent == anon) ? dentry : aparent; | 2120 | dentry->d_parent = (aparent == anon) ? dentry : aparent; |
2028 | list_del(&dentry->d_u.d_child); | 2121 | list_del(&dentry->d_u.d_child); |
2029 | if (!IS_ROOT(dentry)) | 2122 | if (!IS_ROOT(dentry)) |
@@ -2038,6 +2131,10 @@ static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon) | |||
2038 | else | 2131 | else |
2039 | INIT_LIST_HEAD(&anon->d_u.d_child); | 2132 | INIT_LIST_HEAD(&anon->d_u.d_child); |
2040 | 2133 | ||
2134 | dentry_unlock_parents_for_move(anon, dentry); | ||
2135 | spin_unlock(&dentry->d_lock); | ||
2136 | |||
2137 | /* anon->d_lock still locked, returns locked */ | ||
2041 | anon->d_flags &= ~DCACHE_DISCONNECTED; | 2138 | anon->d_flags &= ~DCACHE_DISCONNECTED; |
2042 | } | 2139 | } |
2043 | 2140 | ||
@@ -2073,7 +2170,6 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) | |||
2073 | /* Is this an anonymous mountpoint that we could splice | 2170 | /* Is this an anonymous mountpoint that we could splice |
2074 | * into our tree? */ | 2171 | * into our tree? */ |
2075 | if (IS_ROOT(alias)) { | 2172 | if (IS_ROOT(alias)) { |
2076 | spin_lock(&alias->d_lock); | ||
2077 | __d_materialise_dentry(dentry, alias); | 2173 | __d_materialise_dentry(dentry, alias); |
2078 | __d_drop(alias); | 2174 | __d_drop(alias); |
2079 | goto found; | 2175 | goto found; |
@@ -2558,6 +2654,7 @@ void d_genocide(struct dentry *root) | |||
2558 | struct list_head *next; | 2654 | struct list_head *next; |
2559 | 2655 | ||
2560 | spin_lock(&dcache_lock); | 2656 | spin_lock(&dcache_lock); |
2657 | spin_lock(&this_parent->d_lock); | ||
2561 | repeat: | 2658 | repeat: |
2562 | next = this_parent->d_subdirs.next; | 2659 | next = this_parent->d_subdirs.next; |
2563 | resume: | 2660 | resume: |
@@ -2571,8 +2668,10 @@ resume: | |||
2571 | continue; | 2668 | continue; |
2572 | } | 2669 | } |
2573 | if (!list_empty(&dentry->d_subdirs)) { | 2670 | if (!list_empty(&dentry->d_subdirs)) { |
2574 | spin_unlock(&dentry->d_lock); | 2671 | spin_unlock(&this_parent->d_lock); |
2672 | spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_); | ||
2575 | this_parent = dentry; | 2673 | this_parent = dentry; |
2674 | spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_); | ||
2576 | goto repeat; | 2675 | goto repeat; |
2577 | } | 2676 | } |
2578 | dentry->d_count--; | 2677 | dentry->d_count--; |
@@ -2580,12 +2679,13 @@ resume: | |||
2580 | } | 2679 | } |
2581 | if (this_parent != root) { | 2680 | if (this_parent != root) { |
2582 | next = this_parent->d_u.d_child.next; | 2681 | next = this_parent->d_u.d_child.next; |
2583 | spin_lock(&this_parent->d_lock); | ||
2584 | this_parent->d_count--; | 2682 | this_parent->d_count--; |
2585 | spin_unlock(&this_parent->d_lock); | 2683 | spin_unlock(&this_parent->d_lock); |
2586 | this_parent = this_parent->d_parent; | 2684 | this_parent = this_parent->d_parent; |
2685 | spin_lock(&this_parent->d_lock); | ||
2587 | goto resume; | 2686 | goto resume; |
2588 | } | 2687 | } |
2688 | spin_unlock(&this_parent->d_lock); | ||
2589 | spin_unlock(&dcache_lock); | 2689 | spin_unlock(&dcache_lock); |
2590 | } | 2690 | } |
2591 | 2691 | ||
diff --git a/fs/libfs.c b/fs/libfs.c index 433e7139c23a..cc4794914b52 100644 --- a/fs/libfs.c +++ b/fs/libfs.c | |||
@@ -81,7 +81,8 @@ int dcache_dir_close(struct inode *inode, struct file *file) | |||
81 | 81 | ||
82 | loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) | 82 | loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) |
83 | { | 83 | { |
84 | mutex_lock(&file->f_path.dentry->d_inode->i_mutex); | 84 | struct dentry *dentry = file->f_path.dentry; |
85 | mutex_lock(&dentry->d_inode->i_mutex); | ||
85 | switch (origin) { | 86 | switch (origin) { |
86 | case 1: | 87 | case 1: |
87 | offset += file->f_pos; | 88 | offset += file->f_pos; |
@@ -89,7 +90,7 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) | |||
89 | if (offset >= 0) | 90 | if (offset >= 0) |
90 | break; | 91 | break; |
91 | default: | 92 | default: |
92 | mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); | 93 | mutex_unlock(&dentry->d_inode->i_mutex); |
93 | return -EINVAL; | 94 | return -EINVAL; |
94 | } | 95 | } |
95 | if (offset != file->f_pos) { | 96 | if (offset != file->f_pos) { |
@@ -100,22 +101,25 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) | |||
100 | loff_t n = file->f_pos - 2; | 101 | loff_t n = file->f_pos - 2; |
101 | 102 | ||
102 | spin_lock(&dcache_lock); | 103 | spin_lock(&dcache_lock); |
104 | spin_lock(&dentry->d_lock); | ||
105 | /* d_lock not required for cursor */ | ||
103 | list_del(&cursor->d_u.d_child); | 106 | list_del(&cursor->d_u.d_child); |
104 | p = file->f_path.dentry->d_subdirs.next; | 107 | p = dentry->d_subdirs.next; |
105 | while (n && p != &file->f_path.dentry->d_subdirs) { | 108 | while (n && p != &dentry->d_subdirs) { |
106 | struct dentry *next; | 109 | struct dentry *next; |
107 | next = list_entry(p, struct dentry, d_u.d_child); | 110 | next = list_entry(p, struct dentry, d_u.d_child); |
108 | spin_lock(&next->d_lock); | 111 | spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED); |
109 | if (simple_positive(next)) | 112 | if (simple_positive(next)) |
110 | n--; | 113 | n--; |
111 | spin_unlock(&next->d_lock); | 114 | spin_unlock(&next->d_lock); |
112 | p = p->next; | 115 | p = p->next; |
113 | } | 116 | } |
114 | list_add_tail(&cursor->d_u.d_child, p); | 117 | list_add_tail(&cursor->d_u.d_child, p); |
118 | spin_unlock(&dentry->d_lock); | ||
115 | spin_unlock(&dcache_lock); | 119 | spin_unlock(&dcache_lock); |
116 | } | 120 | } |
117 | } | 121 | } |
118 | mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); | 122 | mutex_unlock(&dentry->d_inode->i_mutex); |
119 | return offset; | 123 | return offset; |
120 | } | 124 | } |
121 | 125 | ||
@@ -156,6 +160,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
156 | /* fallthrough */ | 160 | /* fallthrough */ |
157 | default: | 161 | default: |
158 | spin_lock(&dcache_lock); | 162 | spin_lock(&dcache_lock); |
163 | spin_lock(&dentry->d_lock); | ||
159 | if (filp->f_pos == 2) | 164 | if (filp->f_pos == 2) |
160 | list_move(q, &dentry->d_subdirs); | 165 | list_move(q, &dentry->d_subdirs); |
161 | 166 | ||
@@ -169,6 +174,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
169 | } | 174 | } |
170 | 175 | ||
171 | spin_unlock(&next->d_lock); | 176 | spin_unlock(&next->d_lock); |
177 | spin_unlock(&dentry->d_lock); | ||
172 | spin_unlock(&dcache_lock); | 178 | spin_unlock(&dcache_lock); |
173 | if (filldir(dirent, next->d_name.name, | 179 | if (filldir(dirent, next->d_name.name, |
174 | next->d_name.len, filp->f_pos, | 180 | next->d_name.len, filp->f_pos, |
@@ -176,11 +182,15 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
176 | dt_type(next->d_inode)) < 0) | 182 | dt_type(next->d_inode)) < 0) |
177 | return 0; | 183 | return 0; |
178 | spin_lock(&dcache_lock); | 184 | spin_lock(&dcache_lock); |
185 | spin_lock(&dentry->d_lock); | ||
186 | spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED); | ||
179 | /* next is still alive */ | 187 | /* next is still alive */ |
180 | list_move(q, p); | 188 | list_move(q, p); |
189 | spin_unlock(&next->d_lock); | ||
181 | p = q; | 190 | p = q; |
182 | filp->f_pos++; | 191 | filp->f_pos++; |
183 | } | 192 | } |
193 | spin_unlock(&dentry->d_lock); | ||
184 | spin_unlock(&dcache_lock); | 194 | spin_unlock(&dcache_lock); |
185 | } | 195 | } |
186 | return 0; | 196 | return 0; |
@@ -276,6 +286,7 @@ int simple_empty(struct dentry *dentry) | |||
276 | int ret = 0; | 286 | int ret = 0; |
277 | 287 | ||
278 | spin_lock(&dcache_lock); | 288 | spin_lock(&dcache_lock); |
289 | spin_lock(&dentry->d_lock); | ||
279 | list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) { | 290 | list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) { |
280 | spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); | 291 | spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); |
281 | if (simple_positive(child)) { | 292 | if (simple_positive(child)) { |
@@ -286,6 +297,7 @@ int simple_empty(struct dentry *dentry) | |||
286 | } | 297 | } |
287 | ret = 1; | 298 | ret = 1; |
288 | out: | 299 | out: |
300 | spin_unlock(&dentry->d_lock); | ||
289 | spin_unlock(&dcache_lock); | 301 | spin_unlock(&dcache_lock); |
290 | return ret; | 302 | return ret; |
291 | } | 303 | } |
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index bbbf7922f422..102278ed38bd 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c | |||
@@ -392,6 +392,7 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) | |||
392 | 392 | ||
393 | /* If a pointer is invalid, we search the dentry. */ | 393 | /* If a pointer is invalid, we search the dentry. */ |
394 | spin_lock(&dcache_lock); | 394 | spin_lock(&dcache_lock); |
395 | spin_lock(&parent->d_lock); | ||
395 | next = parent->d_subdirs.next; | 396 | next = parent->d_subdirs.next; |
396 | while (next != &parent->d_subdirs) { | 397 | while (next != &parent->d_subdirs) { |
397 | dent = list_entry(next, struct dentry, d_u.d_child); | 398 | dent = list_entry(next, struct dentry, d_u.d_child); |
@@ -400,11 +401,13 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) | |||
400 | dget_locked(dent); | 401 | dget_locked(dent); |
401 | else | 402 | else |
402 | dent = NULL; | 403 | dent = NULL; |
404 | spin_unlock(&parent->d_lock); | ||
403 | spin_unlock(&dcache_lock); | 405 | spin_unlock(&dcache_lock); |
404 | goto out; | 406 | goto out; |
405 | } | 407 | } |
406 | next = next->next; | 408 | next = next->next; |
407 | } | 409 | } |
410 | spin_unlock(&parent->d_lock); | ||
408 | spin_unlock(&dcache_lock); | 411 | spin_unlock(&dcache_lock); |
409 | return NULL; | 412 | return NULL; |
410 | 413 | ||
diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index 244d1b73fda7..c4b718ff9a6b 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h | |||
@@ -194,6 +194,7 @@ ncp_renew_dentries(struct dentry *parent) | |||
194 | struct dentry *dentry; | 194 | struct dentry *dentry; |
195 | 195 | ||
196 | spin_lock(&dcache_lock); | 196 | spin_lock(&dcache_lock); |
197 | spin_lock(&parent->d_lock); | ||
197 | next = parent->d_subdirs.next; | 198 | next = parent->d_subdirs.next; |
198 | while (next != &parent->d_subdirs) { | 199 | while (next != &parent->d_subdirs) { |
199 | dentry = list_entry(next, struct dentry, d_u.d_child); | 200 | dentry = list_entry(next, struct dentry, d_u.d_child); |
@@ -205,6 +206,7 @@ ncp_renew_dentries(struct dentry *parent) | |||
205 | 206 | ||
206 | next = next->next; | 207 | next = next->next; |
207 | } | 208 | } |
209 | spin_unlock(&parent->d_lock); | ||
208 | spin_unlock(&dcache_lock); | 210 | spin_unlock(&dcache_lock); |
209 | } | 211 | } |
210 | 212 | ||
@@ -216,6 +218,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent) | |||
216 | struct dentry *dentry; | 218 | struct dentry *dentry; |
217 | 219 | ||
218 | spin_lock(&dcache_lock); | 220 | spin_lock(&dcache_lock); |
221 | spin_lock(&parent->d_lock); | ||
219 | next = parent->d_subdirs.next; | 222 | next = parent->d_subdirs.next; |
220 | while (next != &parent->d_subdirs) { | 223 | while (next != &parent->d_subdirs) { |
221 | dentry = list_entry(next, struct dentry, d_u.d_child); | 224 | dentry = list_entry(next, struct dentry, d_u.d_child); |
@@ -223,6 +226,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent) | |||
223 | ncp_age_dentry(server, dentry); | 226 | ncp_age_dentry(server, dentry); |
224 | next = next->next; | 227 | next = next->next; |
225 | } | 228 | } |
229 | spin_unlock(&parent->d_lock); | ||
226 | spin_unlock(&dcache_lock); | 230 | spin_unlock(&dcache_lock); |
227 | } | 231 | } |
228 | 232 | ||
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 20dc218707ca..aa4f25e803f6 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c | |||
@@ -68,17 +68,19 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) | |||
68 | /* run all of the children of the original inode and fix their | 68 | /* run all of the children of the original inode and fix their |
69 | * d_flags to indicate parental interest (their parent is the | 69 | * d_flags to indicate parental interest (their parent is the |
70 | * original inode) */ | 70 | * original inode) */ |
71 | spin_lock(&alias->d_lock); | ||
71 | list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) { | 72 | list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) { |
72 | if (!child->d_inode) | 73 | if (!child->d_inode) |
73 | continue; | 74 | continue; |
74 | 75 | ||
75 | spin_lock(&child->d_lock); | 76 | spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); |
76 | if (watched) | 77 | if (watched) |
77 | child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED; | 78 | child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED; |
78 | else | 79 | else |
79 | child->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED; | 80 | child->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED; |
80 | spin_unlock(&child->d_lock); | 81 | spin_unlock(&child->d_lock); |
81 | } | 82 | } |
83 | spin_unlock(&alias->d_lock); | ||
82 | } | 84 | } |
83 | spin_unlock(&dcache_lock); | 85 | spin_unlock(&dcache_lock); |
84 | } | 86 | } |