aboutsummaryrefslogtreecommitdiffstats
path: root/fs/autofs4
diff options
context:
space:
mode:
authorNick Piggin <npiggin@kernel.dk>2011-01-07 01:49:34 -0500
committerNick Piggin <npiggin@kernel.dk>2011-01-07 01:50:21 -0500
commit2fd6b7f50797f2e993eea59e0a0b8c6399c811dc (patch)
treece33b94b34844c09103836cf4cfa4364b742f217 /fs/autofs4
parentda5029563a0a026c64821b09e8e7b4fd81d3fe1b (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/autofs4')
-rw-r--r--fs/autofs4/autofs_i.h11
-rw-r--r--fs/autofs4/expire.c127
-rw-r--r--fs/autofs4/root.c18
3 files changed, 87 insertions, 69 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
257static 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
257static inline void autofs4_add_expiring(struct dentry *dentry) 268static 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 */
97static struct dentry *next_dentry(struct dentry *p, struct dentry *root) 96static 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);
106relock:
107 p = prev;
108 spin_lock(&p->d_lock);
109again:
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 }
258cont:
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 }
406next: 399next:
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
415found: 404found:
@@ -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
152out: 155out:
@@ -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);
280follow: 287follow:
@@ -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);