diff options
author | Nick Piggin <npiggin@suse.de> | 2010-01-29 18:38:22 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2010-04-27 11:32:33 -0400 |
commit | b9ab2f38fdee96ff49b8a7bbb65cbfc60921e40c (patch) | |
tree | e5e785ba4ae7ec89564914c50638a22d756b6d19 /fs/autofs4 | |
parent | d4fe09131b66c5a7176a5dbfc9bd1ef6939643e8 (diff) |
fs-dcache-scale-d_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).
XXX: probably don't need parent lock in inotify (because child lock
should stabilize parent). Also, possibly some filesystems don't need so
much locking (eg. of child dentry when modifying d_child, so long as
parent is locked)... but be on the safe side. Hmm, maybe we should just
say d_child list is protected by d_parent->d_lock. d_parent could remain
protected with d_lock.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: John Stultz <johnstul@us.ibm.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'fs/autofs4')
-rw-r--r-- | fs/autofs4/expire.c | 85 | ||||
-rw-r--r-- | fs/autofs4/inode.c | 7 | ||||
-rw-r--r-- | fs/autofs4/root.c | 14 |
3 files changed, 78 insertions, 28 deletions
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 47483ca14017..915f6a93bc48 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c | |||
@@ -93,22 +93,59 @@ done: | |||
93 | /* | 93 | /* |
94 | * Calculate next entry in top down tree traversal. | 94 | * Calculate next entry in top down tree traversal. |
95 | * From next_mnt in namespace.c - elegant. | 95 | * From next_mnt in namespace.c - elegant. |
96 | * | ||
97 | * How is this supposed to work if we drop dcache_lock between calls anyway? | ||
98 | * How does it cope with renames? | ||
99 | * And also callers dput the returned dentry before taking dcache_lock again | ||
100 | * so what prevents it from being freed?? | ||
96 | */ | 101 | */ |
97 | static struct dentry *next_dentry(struct dentry *p, struct dentry *root) | 102 | static struct dentry *get_next_positive_dentry(struct dentry *p, |
103 | struct dentry *root) | ||
98 | { | 104 | { |
99 | struct list_head *next = p->d_subdirs.next; | 105 | struct list_head *next; |
106 | struct dentry *ret; | ||
100 | 107 | ||
108 | spin_lock(&dcache_lock); | ||
109 | again: | ||
110 | spin_lock(&p->d_lock); | ||
111 | next = p->d_subdirs.next; | ||
101 | if (next == &p->d_subdirs) { | 112 | if (next == &p->d_subdirs) { |
102 | while (1) { | 113 | while (1) { |
103 | if (p == root) | 114 | struct dentry *parent; |
115 | |||
116 | if (p == root) { | ||
117 | spin_unlock(&p->d_lock); | ||
118 | spin_unlock(&dcache_lock); | ||
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 | goto again; | ||
126 | } | ||
127 | spin_unlock(&p->d_lock); | ||
105 | next = p->d_u.d_child.next; | 128 | next = p->d_u.d_child.next; |
106 | if (next != &p->d_parent->d_subdirs) | 129 | p = parent; |
130 | if (next != &parent->d_subdirs) | ||
107 | break; | 131 | break; |
108 | p = p->d_parent; | ||
109 | } | 132 | } |
110 | } | 133 | } |
111 | return list_entry(next, struct dentry, d_u.d_child); | 134 | ret = list_entry(next, struct dentry, d_u.d_child); |
135 | |||
136 | spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED); | ||
137 | /* Negative dentry - try next */ | ||
138 | if (!simple_positive(ret)) { | ||
139 | spin_unlock(&ret->d_lock); | ||
140 | p = ret; | ||
141 | goto again; | ||
142 | } | ||
143 | dget_dlock(ret); | ||
144 | spin_unlock(&ret->d_lock); | ||
145 | spin_unlock(&p->d_lock); | ||
146 | spin_unlock(&dcache_lock); | ||
147 | |||
148 | return ret; | ||
112 | } | 149 | } |
113 | 150 | ||
114 | /* | 151 | /* |
@@ -158,18 +195,11 @@ static int autofs4_tree_busy(struct vfsmount *mnt, | |||
158 | if (!simple_positive(top)) | 195 | if (!simple_positive(top)) |
159 | return 1; | 196 | return 1; |
160 | 197 | ||
161 | spin_lock(&dcache_lock); | 198 | for (p = dget(top); p; p = get_next_positive_dentry(p, top)) { |
162 | for (p = top; p; p = next_dentry(p, top)) { | ||
163 | /* Negative dentry - give up */ | ||
164 | if (!simple_positive(p)) | ||
165 | continue; | ||
166 | 199 | ||
167 | DPRINTK("dentry %p %.*s", | 200 | DPRINTK("dentry %p %.*s", |
168 | p, (int) p->d_name.len, p->d_name.name); | 201 | p, (int) p->d_name.len, p->d_name.name); |
169 | 202 | ||
170 | p = dget(p); | ||
171 | spin_unlock(&dcache_lock); | ||
172 | |||
173 | /* | 203 | /* |
174 | * Is someone visiting anywhere in the subtree ? | 204 | * Is someone visiting anywhere in the subtree ? |
175 | * If there's no mount we need to check the usage | 205 | * If there's no mount we need to check the usage |
@@ -205,9 +235,7 @@ static int autofs4_tree_busy(struct vfsmount *mnt, | |||
205 | } | 235 | } |
206 | } | 236 | } |
207 | dput(p); | 237 | dput(p); |
208 | spin_lock(&dcache_lock); | ||
209 | } | 238 | } |
210 | spin_unlock(&dcache_lock); | ||
211 | 239 | ||
212 | /* Timeout of a tree mount is ultimately determined by its top dentry */ | 240 | /* Timeout of a tree mount is ultimately determined by its top dentry */ |
213 | if (!autofs4_can_expire(top, timeout, do_now)) | 241 | if (!autofs4_can_expire(top, timeout, do_now)) |
@@ -226,18 +254,11 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, | |||
226 | DPRINTK("parent %p %.*s", | 254 | DPRINTK("parent %p %.*s", |
227 | parent, (int)parent->d_name.len, parent->d_name.name); | 255 | parent, (int)parent->d_name.len, parent->d_name.name); |
228 | 256 | ||
229 | spin_lock(&dcache_lock); | 257 | for (p = dget(parent); p; p = get_next_positive_dentry(p, parent)) { |
230 | for (p = parent; p; p = next_dentry(p, parent)) { | ||
231 | /* Negative dentry - give up */ | ||
232 | if (!simple_positive(p)) | ||
233 | continue; | ||
234 | 258 | ||
235 | DPRINTK("dentry %p %.*s", | 259 | DPRINTK("dentry %p %.*s", |
236 | p, (int) p->d_name.len, p->d_name.name); | 260 | p, (int) p->d_name.len, p->d_name.name); |
237 | 261 | ||
238 | p = dget(p); | ||
239 | spin_unlock(&dcache_lock); | ||
240 | |||
241 | if (d_mountpoint(p)) { | 262 | if (d_mountpoint(p)) { |
242 | /* Can we umount this guy */ | 263 | /* Can we umount this guy */ |
243 | if (autofs4_mount_busy(mnt, p)) | 264 | if (autofs4_mount_busy(mnt, p)) |
@@ -249,9 +270,7 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, | |||
249 | } | 270 | } |
250 | cont: | 271 | cont: |
251 | dput(p); | 272 | dput(p); |
252 | spin_lock(&dcache_lock); | ||
253 | } | 273 | } |
254 | spin_unlock(&dcache_lock); | ||
255 | return NULL; | 274 | return NULL; |
256 | } | 275 | } |
257 | 276 | ||
@@ -295,6 +314,8 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, | |||
295 | * A tree is eligible if :- | 314 | * A tree is eligible if :- |
296 | * - it is unused by any user process | 315 | * - it is unused by any user process |
297 | * - it has been unused for exp_timeout time | 316 | * - it has been unused for exp_timeout time |
317 | * This seems to be racy dropping dcache_lock and asking for next->next after | ||
318 | * the lock has been dropped. | ||
298 | */ | 319 | */ |
299 | struct dentry *autofs4_expire_indirect(struct super_block *sb, | 320 | struct dentry *autofs4_expire_indirect(struct super_block *sb, |
300 | struct vfsmount *mnt, | 321 | struct vfsmount *mnt, |
@@ -317,6 +338,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
317 | timeout = sbi->exp_timeout; | 338 | timeout = sbi->exp_timeout; |
318 | 339 | ||
319 | spin_lock(&dcache_lock); | 340 | spin_lock(&dcache_lock); |
341 | spin_lock(&root->d_lock); | ||
320 | next = root->d_subdirs.next; | 342 | next = root->d_subdirs.next; |
321 | 343 | ||
322 | /* On exit from the loop expire is set to a dgot dentry | 344 | /* On exit from the loop expire is set to a dgot dentry |
@@ -330,7 +352,10 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
330 | continue; | 352 | continue; |
331 | } | 353 | } |
332 | 354 | ||
333 | dentry = dget(dentry); | 355 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); |
356 | dentry = dget_dlock(dentry); | ||
357 | spin_unlock(&dentry->d_lock); | ||
358 | spin_unlock(&root->d_lock); | ||
334 | spin_unlock(&dcache_lock); | 359 | spin_unlock(&dcache_lock); |
335 | 360 | ||
336 | spin_lock(&sbi->fs_lock); | 361 | spin_lock(&sbi->fs_lock); |
@@ -397,8 +422,10 @@ next: | |||
397 | spin_unlock(&sbi->fs_lock); | 422 | spin_unlock(&sbi->fs_lock); |
398 | dput(dentry); | 423 | dput(dentry); |
399 | spin_lock(&dcache_lock); | 424 | spin_lock(&dcache_lock); |
425 | spin_lock(&root->d_lock); | ||
400 | next = next->next; | 426 | next = next->next; |
401 | } | 427 | } |
428 | spin_unlock(&root->d_lock); | ||
402 | spin_unlock(&dcache_lock); | 429 | spin_unlock(&dcache_lock); |
403 | return NULL; | 430 | return NULL; |
404 | 431 | ||
@@ -411,7 +438,11 @@ found: | |||
411 | init_completion(&ino->expire_complete); | 438 | init_completion(&ino->expire_complete); |
412 | spin_unlock(&sbi->fs_lock); | 439 | spin_unlock(&sbi->fs_lock); |
413 | spin_lock(&dcache_lock); | 440 | spin_lock(&dcache_lock); |
441 | spin_lock(&expired->d_parent->d_lock); | ||
442 | spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED); | ||
414 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); | 443 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); |
444 | spin_unlock(&expired->d_lock); | ||
445 | spin_unlock(&expired->d_parent->d_lock); | ||
415 | spin_unlock(&dcache_lock); | 446 | spin_unlock(&dcache_lock); |
416 | return expired; | 447 | return expired; |
417 | } | 448 | } |
diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index d0a3de247458..cf6192583b73 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c | |||
@@ -113,6 +113,7 @@ static void autofs4_force_release(struct autofs_sb_info *sbi) | |||
113 | 113 | ||
114 | spin_lock(&dcache_lock); | 114 | spin_lock(&dcache_lock); |
115 | repeat: | 115 | repeat: |
116 | spin_lock(&this_parent->d_lock); | ||
116 | next = this_parent->d_subdirs.next; | 117 | next = this_parent->d_subdirs.next; |
117 | resume: | 118 | resume: |
118 | while (next != &this_parent->d_subdirs) { | 119 | while (next != &this_parent->d_subdirs) { |
@@ -125,11 +126,13 @@ resume: | |||
125 | } | 126 | } |
126 | 127 | ||
127 | if (!list_empty(&dentry->d_subdirs)) { | 128 | if (!list_empty(&dentry->d_subdirs)) { |
129 | spin_unlock(&this_parent->d_lock); | ||
128 | this_parent = dentry; | 130 | this_parent = dentry; |
129 | goto repeat; | 131 | goto repeat; |
130 | } | 132 | } |
131 | 133 | ||
132 | next = next->next; | 134 | next = next->next; |
135 | spin_unlock(&this_parent->d_lock); | ||
133 | spin_unlock(&dcache_lock); | 136 | spin_unlock(&dcache_lock); |
134 | 137 | ||
135 | DPRINTK("dentry %p %.*s", | 138 | DPRINTK("dentry %p %.*s", |
@@ -137,20 +140,24 @@ resume: | |||
137 | 140 | ||
138 | dput(dentry); | 141 | dput(dentry); |
139 | spin_lock(&dcache_lock); | 142 | spin_lock(&dcache_lock); |
143 | spin_lock(&this_parent->d_lock); | ||
140 | } | 144 | } |
141 | 145 | ||
142 | if (this_parent != sbi->sb->s_root) { | 146 | if (this_parent != sbi->sb->s_root) { |
143 | struct dentry *dentry = this_parent; | 147 | struct dentry *dentry = this_parent; |
144 | 148 | ||
145 | next = this_parent->d_u.d_child.next; | 149 | next = this_parent->d_u.d_child.next; |
150 | spin_unlock(&this_parent->d_lock); | ||
146 | this_parent = this_parent->d_parent; | 151 | this_parent = this_parent->d_parent; |
147 | spin_unlock(&dcache_lock); | 152 | spin_unlock(&dcache_lock); |
148 | DPRINTK("parent dentry %p %.*s", | 153 | DPRINTK("parent dentry %p %.*s", |
149 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | 154 | dentry, (int)dentry->d_name.len, dentry->d_name.name); |
150 | dput(dentry); | 155 | dput(dentry); |
151 | spin_lock(&dcache_lock); | 156 | spin_lock(&dcache_lock); |
157 | spin_lock(&this_parent->d_lock); | ||
152 | goto resume; | 158 | goto resume; |
153 | } | 159 | } |
160 | spin_unlock(&this_parent->d_lock); | ||
154 | spin_unlock(&dcache_lock); | 161 | spin_unlock(&dcache_lock); |
155 | } | 162 | } |
156 | 163 | ||
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 5d5c6d4600bb..9fc3409f9dd9 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
@@ -226,10 +226,13 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) | |||
226 | * it. | 226 | * it. |
227 | */ | 227 | */ |
228 | spin_lock(&dcache_lock); | 228 | spin_lock(&dcache_lock); |
229 | spin_lock(&dentry->d_lock); | ||
229 | if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { | 230 | if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { |
231 | spin_unlock(&dentry->d_lock); | ||
230 | spin_unlock(&dcache_lock); | 232 | spin_unlock(&dcache_lock); |
231 | return -ENOENT; | 233 | return -ENOENT; |
232 | } | 234 | } |
235 | spin_unlock(&dentry->d_lock); | ||
233 | spin_unlock(&dcache_lock); | 236 | spin_unlock(&dcache_lock); |
234 | 237 | ||
235 | out: | 238 | out: |
@@ -311,9 +314,12 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
311 | * multi-mount with no root mount offset. So don't try to | 314 | * multi-mount with no root mount offset. So don't try to |
312 | * mount it again. | 315 | * mount it again. |
313 | */ | 316 | */ |
317 | spin_lock(&dcache_lock); | ||
318 | spin_lock(&dentry->d_lock); | ||
314 | if (ino->flags & AUTOFS_INF_PENDING || | 319 | if (ino->flags & AUTOFS_INF_PENDING || |
315 | (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) { | 320 | (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) { |
316 | ino->flags |= AUTOFS_INF_PENDING; | 321 | ino->flags |= AUTOFS_INF_PENDING; |
322 | spin_unlock(&dentry->d_lock); | ||
317 | spin_unlock(&dcache_lock); | 323 | spin_unlock(&dcache_lock); |
318 | spin_unlock(&sbi->fs_lock); | 324 | spin_unlock(&sbi->fs_lock); |
319 | 325 | ||
@@ -328,6 +334,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
328 | 334 | ||
329 | goto follow; | 335 | goto follow; |
330 | } | 336 | } |
337 | spin_unlock(&dentry->d_lock); | ||
331 | spin_unlock(&dcache_lock); | 338 | spin_unlock(&dcache_lock); |
332 | spin_unlock(&sbi->fs_lock); | 339 | spin_unlock(&sbi->fs_lock); |
333 | follow: | 340 | follow: |
@@ -931,11 +938,16 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) | |||
931 | return -EACCES; | 938 | return -EACCES; |
932 | 939 | ||
933 | spin_lock(&dcache_lock); | 940 | spin_lock(&dcache_lock); |
941 | spin_lock(&dentry->d_lock); | ||
934 | if (!list_empty(&dentry->d_subdirs)) { | 942 | if (!list_empty(&dentry->d_subdirs)) { |
943 | spin_unlock(&dentry->d_lock); | ||
935 | spin_unlock(&dcache_lock); | 944 | spin_unlock(&dcache_lock); |
936 | return -ENOTEMPTY; | 945 | return -ENOTEMPTY; |
937 | } | 946 | } |
938 | spin_lock(&dentry->d_lock); | 947 | spin_lock(&sbi->lookup_lock); |
948 | if (list_empty(&ino->expiring)) | ||
949 | list_add(&ino->expiring, &sbi->expiring_list); | ||
950 | spin_unlock(&sbi->lookup_lock); | ||
939 | __d_drop(dentry); | 951 | __d_drop(dentry); |
940 | spin_unlock(&dentry->d_lock); | 952 | spin_unlock(&dentry->d_lock); |
941 | spin_unlock(&dcache_lock); | 953 | spin_unlock(&dcache_lock); |