Commit da5029563a0a026c64821b09e8e7b4fd81d3fe1b

Authored by Nick Piggin
1 parent b7ab39f631

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);
... ... @@ -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);
... ... @@ -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;
... ... @@ -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 }
... ... @@ -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);
... ... @@ -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 }
security/tomoyo/realpath.c
... ... @@ -14,6 +14,7 @@
14 14 #include <linux/slab.h>
15 15 #include <net/sock.h>
16 16 #include "common.h"
  17 +#include "../../fs/internal.h"
17 18  
18 19 /**
19 20 * tomoyo_encode: Convert binary string to ascii string.