aboutsummaryrefslogtreecommitdiffstats
path: root/fs/autofs4
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2014-10-13 18:52:14 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-13 20:18:16 -0400
commit23bfc2a24ea3d993cc5cc90c9970654e7232502e (patch)
treea2bc0e834578b8b8559e255363e9b95d08158e97 /fs/autofs4
parent8a273345dcb1d74d12f28a0a76320b23e7e32f55 (diff)
autofs4: allow RCU-walk to walk through autofs4
This series teaches autofs about RCU-walk so that we don't drop straight into REF-walk when we hit an autofs directory, and so that we avoid spinlocks as much as possible when performing an RCU-walk. This is needed so that the benefits of the recent NFS support for RCU-walk are fully available when NFS filesystems are automounted. Patches have been carefully reviewed and tested both with test suites and in production - thanks a lot to Ian Kent for his support there. This patch (of 6): Any attempt to look up a pathname that passes though an autofs4 mount is currently forced out of RCU-walk into REF-walk. This can significantly hurt performance of many-thread work loads on many-core systems, especially if the automounted filesystem supports RCU-walk but doesn't get to benefit from it. So if autofs4_d_manage is called with rcu_walk set, only fail with -ECHILD if it is necessary to wait longer than a spinlock. Signed-off-by: NeilBrown <neilb@suse.de> Reviewed-by: Ian Kent <raven@themaw.net> Tested-by: Ian Kent <raven@themaw.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/autofs4')
-rw-r--r--fs/autofs4/autofs_i.h2
-rw-r--r--fs/autofs4/dev-ioctl.c2
-rw-r--r--fs/autofs4/expire.c4
-rw-r--r--fs/autofs4/root.c44
4 files changed, 34 insertions, 18 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index 9e359fb20c0a..2f1032f12d91 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -148,7 +148,7 @@ void autofs4_free_ino(struct autofs_info *);
148 148
149/* Expiration */ 149/* Expiration */
150int is_autofs4_dentry(struct dentry *); 150int is_autofs4_dentry(struct dentry *);
151int autofs4_expire_wait(struct dentry *dentry); 151int autofs4_expire_wait(struct dentry *dentry, int rcu_walk);
152int autofs4_expire_run(struct super_block *, struct vfsmount *, 152int autofs4_expire_run(struct super_block *, struct vfsmount *,
153 struct autofs_sb_info *, 153 struct autofs_sb_info *,
154 struct autofs_packet_expire __user *); 154 struct autofs_packet_expire __user *);
diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
index 5b570b6efa28..aaf96cb25452 100644
--- a/fs/autofs4/dev-ioctl.c
+++ b/fs/autofs4/dev-ioctl.c
@@ -450,7 +450,7 @@ static int autofs_dev_ioctl_requester(struct file *fp,
450 ino = autofs4_dentry_ino(path.dentry); 450 ino = autofs4_dentry_ino(path.dentry);
451 if (ino) { 451 if (ino) {
452 err = 0; 452 err = 0;
453 autofs4_expire_wait(path.dentry); 453 autofs4_expire_wait(path.dentry, 0);
454 spin_lock(&sbi->fs_lock); 454 spin_lock(&sbi->fs_lock);
455 param->requester.uid = from_kuid_munged(current_user_ns(), ino->uid); 455 param->requester.uid = from_kuid_munged(current_user_ns(), ino->uid);
456 param->requester.gid = from_kgid_munged(current_user_ns(), ino->gid); 456 param->requester.gid = from_kgid_munged(current_user_ns(), ino->gid);
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 8fa3895cda02..1695eda8ac18 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -461,7 +461,7 @@ found:
461 return expired; 461 return expired;
462} 462}
463 463
464int autofs4_expire_wait(struct dentry *dentry) 464int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
465{ 465{
466 struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); 466 struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
467 struct autofs_info *ino = autofs4_dentry_ino(dentry); 467 struct autofs_info *ino = autofs4_dentry_ino(dentry);
@@ -471,6 +471,8 @@ int autofs4_expire_wait(struct dentry *dentry)
471 spin_lock(&sbi->fs_lock); 471 spin_lock(&sbi->fs_lock);
472 if (ino->flags & AUTOFS_INF_EXPIRING) { 472 if (ino->flags & AUTOFS_INF_EXPIRING) {
473 spin_unlock(&sbi->fs_lock); 473 spin_unlock(&sbi->fs_lock);
474 if (rcu_walk)
475 return -ECHILD;
474 476
475 DPRINTK("waiting for expire %p name=%.*s", 477 DPRINTK("waiting for expire %p name=%.*s",
476 dentry, dentry->d_name.len, dentry->d_name.name); 478 dentry, dentry->d_name.len, dentry->d_name.name);
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index cdb25ebccc4c..2296c8301b66 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -210,7 +210,8 @@ next:
210 return NULL; 210 return NULL;
211} 211}
212 212
213static struct dentry *autofs4_lookup_expiring(struct dentry *dentry) 213static struct dentry *autofs4_lookup_expiring(struct dentry *dentry,
214 bool rcu_walk)
214{ 215{
215 struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); 216 struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
216 struct dentry *parent = dentry->d_parent; 217 struct dentry *parent = dentry->d_parent;
@@ -229,6 +230,11 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
229 struct dentry *expiring; 230 struct dentry *expiring;
230 struct qstr *qstr; 231 struct qstr *qstr;
231 232
233 if (rcu_walk) {
234 spin_unlock(&sbi->lookup_lock);
235 return ERR_PTR(-ECHILD);
236 }
237
232 ino = list_entry(p, struct autofs_info, expiring); 238 ino = list_entry(p, struct autofs_info, expiring);
233 expiring = ino->dentry; 239 expiring = ino->dentry;
234 240
@@ -264,13 +270,15 @@ next:
264 return NULL; 270 return NULL;
265} 271}
266 272
267static int autofs4_mount_wait(struct dentry *dentry) 273static int autofs4_mount_wait(struct dentry *dentry, bool rcu_walk)
268{ 274{
269 struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); 275 struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
270 struct autofs_info *ino = autofs4_dentry_ino(dentry); 276 struct autofs_info *ino = autofs4_dentry_ino(dentry);
271 int status = 0; 277 int status = 0;
272 278
273 if (ino->flags & AUTOFS_INF_PENDING) { 279 if (ino->flags & AUTOFS_INF_PENDING) {
280 if (rcu_walk)
281 return -ECHILD;
274 DPRINTK("waiting for mount name=%.*s", 282 DPRINTK("waiting for mount name=%.*s",
275 dentry->d_name.len, dentry->d_name.name); 283 dentry->d_name.len, dentry->d_name.name);
276 status = autofs4_wait(sbi, dentry, NFY_MOUNT); 284 status = autofs4_wait(sbi, dentry, NFY_MOUNT);
@@ -280,20 +288,22 @@ static int autofs4_mount_wait(struct dentry *dentry)
280 return status; 288 return status;
281} 289}
282 290
283static int do_expire_wait(struct dentry *dentry) 291static int do_expire_wait(struct dentry *dentry, bool rcu_walk)
284{ 292{
285 struct dentry *expiring; 293 struct dentry *expiring;
286 294
287 expiring = autofs4_lookup_expiring(dentry); 295 expiring = autofs4_lookup_expiring(dentry, rcu_walk);
296 if (IS_ERR(expiring))
297 return PTR_ERR(expiring);
288 if (!expiring) 298 if (!expiring)
289 return autofs4_expire_wait(dentry); 299 return autofs4_expire_wait(dentry, rcu_walk);
290 else { 300 else {
291 /* 301 /*
292 * If we are racing with expire the request might not 302 * If we are racing with expire the request might not
293 * be quite complete, but the directory has been removed 303 * be quite complete, but the directory has been removed
294 * so it must have been successful, just wait for it. 304 * so it must have been successful, just wait for it.
295 */ 305 */
296 autofs4_expire_wait(expiring); 306 autofs4_expire_wait(expiring, 0);
297 autofs4_del_expiring(expiring); 307 autofs4_del_expiring(expiring);
298 dput(expiring); 308 dput(expiring);
299 } 309 }
@@ -345,7 +355,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
345 * and the directory was removed, so just go ahead and try 355 * and the directory was removed, so just go ahead and try
346 * the mount. 356 * the mount.
347 */ 357 */
348 status = do_expire_wait(dentry); 358 status = do_expire_wait(dentry, 0);
349 if (status && status != -EAGAIN) 359 if (status && status != -EAGAIN)
350 return NULL; 360 return NULL;
351 361
@@ -353,7 +363,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
353 spin_lock(&sbi->fs_lock); 363 spin_lock(&sbi->fs_lock);
354 if (ino->flags & AUTOFS_INF_PENDING) { 364 if (ino->flags & AUTOFS_INF_PENDING) {
355 spin_unlock(&sbi->fs_lock); 365 spin_unlock(&sbi->fs_lock);
356 status = autofs4_mount_wait(dentry); 366 status = autofs4_mount_wait(dentry, 0);
357 if (status) 367 if (status)
358 return ERR_PTR(status); 368 return ERR_PTR(status);
359 goto done; 369 goto done;
@@ -394,7 +404,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
394 } 404 }
395 ino->flags |= AUTOFS_INF_PENDING; 405 ino->flags |= AUTOFS_INF_PENDING;
396 spin_unlock(&sbi->fs_lock); 406 spin_unlock(&sbi->fs_lock);
397 status = autofs4_mount_wait(dentry); 407 status = autofs4_mount_wait(dentry, 0);
398 spin_lock(&sbi->fs_lock); 408 spin_lock(&sbi->fs_lock);
399 ino->flags &= ~AUTOFS_INF_PENDING; 409 ino->flags &= ~AUTOFS_INF_PENDING;
400 if (status) { 410 if (status) {
@@ -430,21 +440,25 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
430 return 0; 440 return 0;
431 } 441 }
432 442
433 /* We need to sleep, so we need pathwalk to be in ref-mode */
434 if (rcu_walk)
435 return -ECHILD;
436
437 /* Wait for pending expires */ 443 /* Wait for pending expires */
438 do_expire_wait(dentry); 444 if (do_expire_wait(dentry, rcu_walk) == -ECHILD)
445 return -ECHILD;
439 446
440 /* 447 /*
441 * This dentry may be under construction so wait on mount 448 * This dentry may be under construction so wait on mount
442 * completion. 449 * completion.
443 */ 450 */
444 status = autofs4_mount_wait(dentry); 451 status = autofs4_mount_wait(dentry, rcu_walk);
445 if (status) 452 if (status)
446 return status; 453 return status;
447 454
455 if (rcu_walk)
456 /* it is always safe to return 0 as the worst that
457 * will happen is we retry in REF-walk mode.
458 * Better than always taking a lock.
459 */
460 return 0;
461
448 spin_lock(&sbi->fs_lock); 462 spin_lock(&sbi->fs_lock);
449 /* 463 /*
450 * If the dentry has been selected for expire while we slept 464 * If the dentry has been selected for expire while we slept