Commit da5029563a0a026c64821b09e8e7b4fd81d3fe1b
1 parent
b7ab39f631
Exists in
master
and in
4 other branches
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>
Showing 10 changed files with 102 additions and 54 deletions Side-by-side Diff
arch/powerpc/platforms/cell/spufs/inode.c
... | ... | @@ -166,6 +166,9 @@ |
166 | 166 | __d_drop(dentry); |
167 | 167 | spin_unlock(&dentry->d_lock); |
168 | 168 | simple_unlink(dir->d_inode, dentry); |
169 | + /* XXX: what is dcache_lock protecting here? Other | |
170 | + * filesystems (IB, configfs) release dcache_lock | |
171 | + * before unlink */ | |
169 | 172 | spin_unlock(&dcache_lock); |
170 | 173 | dput(dentry); |
171 | 174 | } else { |
drivers/usb/core/inode.c
... | ... | @@ -347,10 +347,13 @@ |
347 | 347 | |
348 | 348 | list_for_each(list, &dentry->d_subdirs) { |
349 | 349 | struct dentry *de = list_entry(list, struct dentry, d_u.d_child); |
350 | + spin_lock(&de->d_lock); | |
350 | 351 | if (usbfs_positive(de)) { |
352 | + spin_unlock(&de->d_lock); | |
351 | 353 | spin_unlock(&dcache_lock); |
352 | 354 | return 0; |
353 | 355 | } |
356 | + spin_unlock(&de->d_lock); | |
354 | 357 | } |
355 | 358 | |
356 | 359 | spin_unlock(&dcache_lock); |
fs/autofs4/autofs_i.h
... | ... | @@ -254,19 +254,6 @@ |
254 | 254 | return dentry->d_inode && !d_unhashed(dentry); |
255 | 255 | } |
256 | 256 | |
257 | -static inline int __simple_empty(struct dentry *dentry) | |
258 | -{ | |
259 | - struct dentry *child; | |
260 | - int ret = 0; | |
261 | - | |
262 | - list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) | |
263 | - if (simple_positive(child)) | |
264 | - goto out; | |
265 | - ret = 1; | |
266 | -out: | |
267 | - return ret; | |
268 | -} | |
269 | - | |
270 | 257 | static inline void autofs4_add_expiring(struct dentry *dentry) |
271 | 258 | { |
272 | 259 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); |
fs/autofs4/expire.c
... | ... | @@ -160,14 +160,18 @@ |
160 | 160 | |
161 | 161 | spin_lock(&dcache_lock); |
162 | 162 | for (p = top; p; p = next_dentry(p, top)) { |
163 | + spin_lock(&p->d_lock); | |
163 | 164 | /* Negative dentry - give up */ |
164 | - if (!simple_positive(p)) | |
165 | + if (!simple_positive(p)) { | |
166 | + spin_unlock(&p->d_lock); | |
165 | 167 | continue; |
168 | + } | |
166 | 169 | |
167 | 170 | DPRINTK("dentry %p %.*s", |
168 | 171 | p, (int) p->d_name.len, p->d_name.name); |
169 | 172 | |
170 | - p = dget(p); | |
173 | + p = dget_dlock(p); | |
174 | + spin_unlock(&p->d_lock); | |
171 | 175 | spin_unlock(&dcache_lock); |
172 | 176 | |
173 | 177 | /* |
174 | 178 | |
175 | 179 | |
176 | 180 | |
... | ... | @@ -228,14 +232,18 @@ |
228 | 232 | |
229 | 233 | spin_lock(&dcache_lock); |
230 | 234 | for (p = parent; p; p = next_dentry(p, parent)) { |
235 | + spin_lock(&p->d_lock); | |
231 | 236 | /* Negative dentry - give up */ |
232 | - if (!simple_positive(p)) | |
237 | + if (!simple_positive(p)) { | |
238 | + spin_unlock(&p->d_lock); | |
233 | 239 | continue; |
240 | + } | |
234 | 241 | |
235 | 242 | DPRINTK("dentry %p %.*s", |
236 | 243 | p, (int) p->d_name.len, p->d_name.name); |
237 | 244 | |
238 | - p = dget(p); | |
245 | + p = dget_dlock(p); | |
246 | + spin_unlock(&p->d_lock); | |
239 | 247 | spin_unlock(&dcache_lock); |
240 | 248 | |
241 | 249 | if (d_mountpoint(p)) { |
242 | 250 | |
243 | 251 | |
... | ... | @@ -324,12 +332,15 @@ |
324 | 332 | struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); |
325 | 333 | |
326 | 334 | /* Negative dentry - give up */ |
335 | + spin_lock(&dentry->d_lock); | |
327 | 336 | if (!simple_positive(dentry)) { |
328 | 337 | next = next->next; |
338 | + spin_unlock(&dentry->d_lock); | |
329 | 339 | continue; |
330 | 340 | } |
331 | 341 | |
332 | - dentry = dget(dentry); | |
342 | + dentry = dget_dlock(dentry); | |
343 | + spin_unlock(&dentry->d_lock); | |
333 | 344 | spin_unlock(&dcache_lock); |
334 | 345 | |
335 | 346 | spin_lock(&sbi->fs_lock); |
fs/ceph/dir.c
... | ... | @@ -136,6 +136,7 @@ |
136 | 136 | fi->at_end = 1; |
137 | 137 | goto out_unlock; |
138 | 138 | } |
139 | + spin_lock(&dentry->d_lock); | |
139 | 140 | if (!d_unhashed(dentry) && dentry->d_inode && |
140 | 141 | ceph_snap(dentry->d_inode) != CEPH_SNAPDIR && |
141 | 142 | ceph_ino(dentry->d_inode) != CEPH_INO_CEPH && |
142 | 143 | |
... | ... | @@ -145,13 +146,13 @@ |
145 | 146 | dentry->d_name.len, dentry->d_name.name, di->offset, |
146 | 147 | filp->f_pos, d_unhashed(dentry) ? " unhashed" : "", |
147 | 148 | !dentry->d_inode ? " null" : ""); |
149 | + spin_unlock(&dentry->d_lock); | |
148 | 150 | p = p->prev; |
149 | 151 | dentry = list_entry(p, struct dentry, d_u.d_child); |
150 | 152 | di = ceph_dentry(dentry); |
151 | 153 | } |
152 | 154 | |
153 | - spin_lock(&dentry->d_lock); | |
154 | - dentry->d_count++; | |
155 | + dget_dlock(dentry); | |
155 | 156 | spin_unlock(&dentry->d_lock); |
156 | 157 | spin_unlock(&dcache_lock); |
157 | 158 |
fs/configfs/configfs_internal.h
... | ... | @@ -121,6 +121,7 @@ |
121 | 121 | struct config_item * item = NULL; |
122 | 122 | |
123 | 123 | spin_lock(&dcache_lock); |
124 | + spin_lock(&dentry->d_lock); | |
124 | 125 | if (!d_unhashed(dentry)) { |
125 | 126 | struct configfs_dirent * sd = dentry->d_fsdata; |
126 | 127 | if (sd->s_type & CONFIGFS_ITEM_LINK) { |
... | ... | @@ -129,6 +130,7 @@ |
129 | 130 | } else |
130 | 131 | item = config_item_get(sd->s_element); |
131 | 132 | } |
133 | + spin_unlock(&dentry->d_lock); | |
132 | 134 | spin_unlock(&dcache_lock); |
133 | 135 | |
134 | 136 | return item; |
fs/dcache.c
... | ... | @@ -46,6 +46,7 @@ |
46 | 46 | * - d_name |
47 | 47 | * - d_lru |
48 | 48 | * - d_count |
49 | + * - d_unhashed() | |
49 | 50 | * |
50 | 51 | * Ordering: |
51 | 52 | * dcache_lock |
... | ... | @@ -53,6 +54,13 @@ |
53 | 54 | * dcache_lru_lock |
54 | 55 | * dcache_hash_lock |
55 | 56 | * |
57 | + * If there is an ancestor relationship: | |
58 | + * dentry->d_parent->...->d_parent->d_lock | |
59 | + * ... | |
60 | + * dentry->d_parent->d_lock | |
61 | + * dentry->d_lock | |
62 | + * | |
63 | + * If no ancestor relationship: | |
56 | 64 | * if (dentry1 < dentry2) |
57 | 65 | * dentry1->d_lock |
58 | 66 | * dentry2->d_lock |
59 | 67 | |
... | ... | @@ -379,7 +387,9 @@ |
379 | 387 | * If it's already been dropped, return OK. |
380 | 388 | */ |
381 | 389 | spin_lock(&dcache_lock); |
390 | + spin_lock(&dentry->d_lock); | |
382 | 391 | if (d_unhashed(dentry)) { |
392 | + spin_unlock(&dentry->d_lock); | |
383 | 393 | spin_unlock(&dcache_lock); |
384 | 394 | return 0; |
385 | 395 | } |
386 | 396 | |
... | ... | @@ -388,9 +398,11 @@ |
388 | 398 | * to get rid of unused child entries. |
389 | 399 | */ |
390 | 400 | if (!list_empty(&dentry->d_subdirs)) { |
401 | + spin_unlock(&dentry->d_lock); | |
391 | 402 | spin_unlock(&dcache_lock); |
392 | 403 | shrink_dcache_parent(dentry); |
393 | 404 | spin_lock(&dcache_lock); |
405 | + spin_lock(&dentry->d_lock); | |
394 | 406 | } |
395 | 407 | |
396 | 408 | /* |
... | ... | @@ -403,7 +415,6 @@ |
403 | 415 | * we might still populate it if it was a |
404 | 416 | * working directory or similar). |
405 | 417 | */ |
406 | - spin_lock(&dentry->d_lock); | |
407 | 418 | if (dentry->d_count > 1) { |
408 | 419 | if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { |
409 | 420 | spin_unlock(&dentry->d_lock); |
410 | 421 | |
411 | 422 | |
412 | 423 | |
413 | 424 | |
414 | 425 | |
415 | 426 | |
416 | 427 | |
... | ... | @@ -490,35 +501,44 @@ |
490 | 501 | * any other hashed alias over that one unless @want_discon is set, |
491 | 502 | * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias. |
492 | 503 | */ |
493 | - | |
494 | -static struct dentry * __d_find_alias(struct inode *inode, int want_discon) | |
504 | +static struct dentry *__d_find_alias(struct inode *inode, int want_discon) | |
495 | 505 | { |
496 | - struct list_head *head, *next, *tmp; | |
497 | - struct dentry *alias, *discon_alias=NULL; | |
506 | + struct dentry *alias, *discon_alias; | |
498 | 507 | |
499 | - head = &inode->i_dentry; | |
500 | - next = inode->i_dentry.next; | |
501 | - while (next != head) { | |
502 | - tmp = next; | |
503 | - next = tmp->next; | |
504 | - prefetch(next); | |
505 | - alias = list_entry(tmp, struct dentry, d_alias); | |
508 | +again: | |
509 | + discon_alias = NULL; | |
510 | + list_for_each_entry(alias, &inode->i_dentry, d_alias) { | |
511 | + spin_lock(&alias->d_lock); | |
506 | 512 | if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) { |
507 | 513 | if (IS_ROOT(alias) && |
508 | - (alias->d_flags & DCACHE_DISCONNECTED)) | |
514 | + (alias->d_flags & DCACHE_DISCONNECTED)) { | |
509 | 515 | discon_alias = alias; |
510 | - else if (!want_discon) { | |
511 | - __dget_locked(alias); | |
516 | + } else if (!want_discon) { | |
517 | + __dget_locked_dlock(alias); | |
518 | + spin_unlock(&alias->d_lock); | |
512 | 519 | return alias; |
513 | 520 | } |
514 | 521 | } |
522 | + spin_unlock(&alias->d_lock); | |
515 | 523 | } |
516 | - if (discon_alias) | |
517 | - __dget_locked(discon_alias); | |
518 | - return discon_alias; | |
524 | + if (discon_alias) { | |
525 | + alias = discon_alias; | |
526 | + spin_lock(&alias->d_lock); | |
527 | + if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) { | |
528 | + if (IS_ROOT(alias) && | |
529 | + (alias->d_flags & DCACHE_DISCONNECTED)) { | |
530 | + __dget_locked_dlock(alias); | |
531 | + spin_unlock(&alias->d_lock); | |
532 | + return alias; | |
533 | + } | |
534 | + } | |
535 | + spin_unlock(&alias->d_lock); | |
536 | + goto again; | |
537 | + } | |
538 | + return NULL; | |
519 | 539 | } |
520 | 540 | |
521 | -struct dentry * d_find_alias(struct inode *inode) | |
541 | +struct dentry *d_find_alias(struct inode *inode) | |
522 | 542 | { |
523 | 543 | struct dentry *de = NULL; |
524 | 544 | |
525 | 545 | |
... | ... | @@ -801,8 +821,8 @@ |
801 | 821 | spin_lock(&dcache_lock); |
802 | 822 | spin_lock(&dentry->d_lock); |
803 | 823 | dentry_lru_del(dentry); |
804 | - spin_unlock(&dentry->d_lock); | |
805 | 824 | __d_drop(dentry); |
825 | + spin_unlock(&dentry->d_lock); | |
806 | 826 | spin_unlock(&dcache_lock); |
807 | 827 | |
808 | 828 | for (;;) { |
809 | 829 | |
... | ... | @@ -817,8 +837,8 @@ |
817 | 837 | d_u.d_child) { |
818 | 838 | spin_lock(&loop->d_lock); |
819 | 839 | dentry_lru_del(loop); |
820 | - spin_unlock(&loop->d_lock); | |
821 | 840 | __d_drop(loop); |
841 | + spin_unlock(&loop->d_lock); | |
822 | 842 | cond_resched_lock(&dcache_lock); |
823 | 843 | } |
824 | 844 | spin_unlock(&dcache_lock); |
... | ... | @@ -1863,7 +1883,10 @@ |
1863 | 1883 | /* |
1864 | 1884 | * XXXX: do we really need to take target->d_lock? |
1865 | 1885 | */ |
1866 | - if (target < dentry) { | |
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) { | |
1867 | 1890 | spin_lock(&target->d_lock); |
1868 | 1891 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); |
1869 | 1892 | } else { |
1870 | 1893 | |
1871 | 1894 | |
1872 | 1895 | |
... | ... | @@ -2542,13 +2565,16 @@ |
2542 | 2565 | struct list_head *tmp = next; |
2543 | 2566 | struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); |
2544 | 2567 | next = tmp->next; |
2545 | - if (d_unhashed(dentry)||!dentry->d_inode) | |
2568 | + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); | |
2569 | + if (d_unhashed(dentry) || !dentry->d_inode) { | |
2570 | + spin_unlock(&dentry->d_lock); | |
2546 | 2571 | continue; |
2572 | + } | |
2547 | 2573 | if (!list_empty(&dentry->d_subdirs)) { |
2574 | + spin_unlock(&dentry->d_lock); | |
2548 | 2575 | this_parent = dentry; |
2549 | 2576 | goto repeat; |
2550 | 2577 | } |
2551 | - spin_lock(&dentry->d_lock); | |
2552 | 2578 | dentry->d_count--; |
2553 | 2579 | spin_unlock(&dentry->d_lock); |
2554 | 2580 | } |
fs/libfs.c
... | ... | @@ -16,6 +16,11 @@ |
16 | 16 | |
17 | 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 | 24 | int simple_getattr(struct vfsmount *mnt, struct dentry *dentry, |
20 | 25 | struct kstat *stat) |
21 | 26 | { |
22 | 27 | |
... | ... | @@ -100,8 +105,10 @@ |
100 | 105 | while (n && p != &file->f_path.dentry->d_subdirs) { |
101 | 106 | struct dentry *next; |
102 | 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 | 110 | n--; |
111 | + spin_unlock(&next->d_lock); | |
105 | 112 | p = p->next; |
106 | 113 | } |
107 | 114 | list_add_tail(&cursor->d_u.d_child, p); |
108 | 115 | |
109 | 116 | |
... | ... | @@ -155,9 +162,13 @@ |
155 | 162 | for (p=q->next; p != &dentry->d_subdirs; p=p->next) { |
156 | 163 | struct dentry *next; |
157 | 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 | 168 | continue; |
169 | + } | |
160 | 170 | |
171 | + spin_unlock(&next->d_lock); | |
161 | 172 | spin_unlock(&dcache_lock); |
162 | 173 | if (filldir(dirent, next->d_name.name, |
163 | 174 | next->d_name.len, filp->f_pos, |
164 | 175 | |
165 | 176 | |
... | ... | @@ -259,20 +270,20 @@ |
259 | 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 | 273 | int simple_empty(struct dentry *dentry) |
268 | 274 | { |
269 | 275 | struct dentry *child; |
270 | 276 | int ret = 0; |
271 | 277 | |
272 | 278 | spin_lock(&dcache_lock); |
273 | - list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) | |
274 | - if (simple_positive(child)) | |
279 | + list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) { | |
280 | + spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); | |
281 | + if (simple_positive(child)) { | |
282 | + spin_unlock(&child->d_lock); | |
275 | 283 | goto out; |
284 | + } | |
285 | + spin_unlock(&child->d_lock); | |
286 | + } | |
276 | 287 | ret = 1; |
277 | 288 | out: |
278 | 289 | spin_unlock(&dcache_lock); |
fs/ocfs2/dcache.c
... | ... | @@ -174,13 +174,16 @@ |
174 | 174 | list_for_each(p, &inode->i_dentry) { |
175 | 175 | dentry = list_entry(p, struct dentry, d_alias); |
176 | 176 | |
177 | + spin_lock(&dentry->d_lock); | |
177 | 178 | if (ocfs2_match_dentry(dentry, parent_blkno, skip_unhashed)) { |
178 | 179 | mlog(0, "dentry found: %.*s\n", |
179 | 180 | dentry->d_name.len, dentry->d_name.name); |
180 | 181 | |
181 | - dget_locked(dentry); | |
182 | + dget_locked_dlock(dentry); | |
183 | + spin_unlock(&dentry->d_lock); | |
182 | 184 | break; |
183 | 185 | } |
186 | + spin_unlock(&dentry->d_lock); | |
184 | 187 | |
185 | 188 | dentry = NULL; |
186 | 189 | } |