aboutsummaryrefslogtreecommitdiffstats
path: root/fs/autofs4/expire.c
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/expire.c
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/expire.c')
-rw-r--r--fs/autofs4/expire.c127
1 files changed, 60 insertions, 67 deletions
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}