diff options
| author | Ian Kent <raven@themaw.net> | 2008-07-24 00:30:27 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-24 13:47:32 -0400 |
| commit | 6e60a9ab5f5d314735467752f623072f5b75157a (patch) | |
| tree | 1a42df40e6fad52bd6be74970af8eb56870fe6ba /fs/autofs4 | |
| parent | 97e7449a7ad883bf9f516fc970778d75999c7843 (diff) | |
autofs4: fix direct mount pending expire race
For direct and offset type mounts that are covered by another mount we
cannot check the AUTOFS_INF_EXPIRING flag during a path walk which leads
to lookups walking into an expiring mount while it is being expired.
For example, for the direct multi-mount map entry with a couple of
offsets:
/race/mm1 / <server1>:/<path1>
/om1 <server2>:/<path2>
/om2 <server1>:/<path3>
an autofs trigger mount is mounted on /race/mm1 and when accessed it is
over mounted and trigger mounts made for /race/mm1/om1 and /race/mm1/om2.
So it isn't possible for path walks to see the expiring flag at all and
they happily walk into the file system while it is expiring.
When expiring these mounts follow_down() must stop at the autofs mount and
all processes must block in the ->follow_link() method (except the daemon)
until the expire is complete. This is done by decrementing the d_mounted
field of the autofs trigger mount root dentry until the expire is
completed. In ->follow_link() all processes wait on the expire and the
mount following is completed for the daemon until the expire is complete.
Signed-off-by: Ian Kent <raven@themaw.net>
Cc: Jeff Moyer <jmoyer@redhat.com>
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.h | 3 | ||||
| -rw-r--r-- | fs/autofs4/expire.c | 16 | ||||
| -rw-r--r-- | fs/autofs4/root.c | 72 |
3 files changed, 65 insertions, 26 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 5d90ed3b4b43..4b40cbc71e9b 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h | |||
| @@ -52,6 +52,8 @@ struct autofs_info { | |||
| 52 | 52 | ||
| 53 | int flags; | 53 | int flags; |
| 54 | 54 | ||
| 55 | struct completion expire_complete; | ||
| 56 | |||
| 55 | struct list_head active; | 57 | struct list_head active; |
| 56 | struct list_head expiring; | 58 | struct list_head expiring; |
| 57 | 59 | ||
| @@ -69,6 +71,7 @@ struct autofs_info { | |||
| 69 | }; | 71 | }; |
| 70 | 72 | ||
| 71 | #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ | 73 | #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ |
| 74 | #define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ | ||
| 72 | 75 | ||
| 73 | struct autofs_wait_queue { | 76 | struct autofs_wait_queue { |
| 74 | wait_queue_head_t queue; | 77 | wait_queue_head_t queue; |
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 19f5bea2704f..705b9f057fb3 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c | |||
| @@ -259,13 +259,15 @@ static struct dentry *autofs4_expire_direct(struct super_block *sb, | |||
| 259 | now = jiffies; | 259 | now = jiffies; |
| 260 | timeout = sbi->exp_timeout; | 260 | timeout = sbi->exp_timeout; |
| 261 | 261 | ||
| 262 | /* Lock the tree as we must expire as a whole */ | ||
| 263 | spin_lock(&sbi->fs_lock); | 262 | spin_lock(&sbi->fs_lock); |
| 264 | if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { | 263 | if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { |
| 265 | struct autofs_info *ino = autofs4_dentry_ino(root); | 264 | struct autofs_info *ino = autofs4_dentry_ino(root); |
| 266 | 265 | if (d_mountpoint(root)) { | |
| 267 | /* Set this flag early to catch sys_chdir and the like */ | 266 | ino->flags |= AUTOFS_INF_MOUNTPOINT; |
| 267 | root->d_mounted--; | ||
| 268 | } | ||
| 268 | ino->flags |= AUTOFS_INF_EXPIRING; | 269 | ino->flags |= AUTOFS_INF_EXPIRING; |
| 270 | init_completion(&ino->expire_complete); | ||
| 269 | spin_unlock(&sbi->fs_lock); | 271 | spin_unlock(&sbi->fs_lock); |
| 270 | return root; | 272 | return root; |
| 271 | } | 273 | } |
| @@ -392,6 +394,7 @@ found: | |||
| 392 | expired, (int)expired->d_name.len, expired->d_name.name); | 394 | expired, (int)expired->d_name.len, expired->d_name.name); |
| 393 | ino = autofs4_dentry_ino(expired); | 395 | ino = autofs4_dentry_ino(expired); |
| 394 | ino->flags |= AUTOFS_INF_EXPIRING; | 396 | ino->flags |= AUTOFS_INF_EXPIRING; |
| 397 | init_completion(&ino->expire_complete); | ||
| 395 | spin_unlock(&sbi->fs_lock); | 398 | spin_unlock(&sbi->fs_lock); |
| 396 | spin_lock(&dcache_lock); | 399 | spin_lock(&dcache_lock); |
| 397 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); | 400 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); |
| @@ -429,6 +432,7 @@ int autofs4_expire_run(struct super_block *sb, | |||
| 429 | spin_lock(&sbi->fs_lock); | 432 | spin_lock(&sbi->fs_lock); |
| 430 | ino = autofs4_dentry_ino(dentry); | 433 | ino = autofs4_dentry_ino(dentry); |
| 431 | ino->flags &= ~AUTOFS_INF_EXPIRING; | 434 | ino->flags &= ~AUTOFS_INF_EXPIRING; |
| 435 | complete_all(&ino->expire_complete); | ||
| 432 | spin_unlock(&sbi->fs_lock); | 436 | spin_unlock(&sbi->fs_lock); |
| 433 | 437 | ||
| 434 | return ret; | 438 | return ret; |
| @@ -457,8 +461,14 @@ int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, | |||
| 457 | /* This is synchronous because it makes the daemon a | 461 | /* This is synchronous because it makes the daemon a |
| 458 | little easier */ | 462 | little easier */ |
| 459 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); | 463 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); |
| 464 | |||
| 460 | spin_lock(&sbi->fs_lock); | 465 | spin_lock(&sbi->fs_lock); |
| 466 | if (ino->flags & AUTOFS_INF_MOUNTPOINT) { | ||
| 467 | sb->s_root->d_mounted++; | ||
| 468 | ino->flags &= ~AUTOFS_INF_MOUNTPOINT; | ||
| 469 | } | ||
| 461 | ino->flags &= ~AUTOFS_INF_EXPIRING; | 470 | ino->flags &= ~AUTOFS_INF_EXPIRING; |
| 471 | complete_all(&ino->expire_complete); | ||
| 462 | spin_unlock(&sbi->fs_lock); | 472 | spin_unlock(&sbi->fs_lock); |
| 463 | dput(dentry); | 473 | dput(dentry); |
| 464 | } | 474 | } |
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 1c2579de1f2e..adbd8559e870 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
| @@ -141,6 +141,7 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags) | |||
| 141 | dentry, dentry->d_name.len, dentry->d_name.name); | 141 | dentry, dentry->d_name.len, dentry->d_name.name); |
| 142 | 142 | ||
| 143 | status = autofs4_wait(sbi, dentry, NFY_NONE); | 143 | status = autofs4_wait(sbi, dentry, NFY_NONE); |
| 144 | wait_for_completion(&ino->expire_complete); | ||
| 144 | 145 | ||
| 145 | DPRINTK("expire done status=%d", status); | 146 | DPRINTK("expire done status=%d", status); |
| 146 | 147 | ||
| @@ -227,14 +228,32 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
| 227 | DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", | 228 | DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", |
| 228 | dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, | 229 | dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, |
| 229 | nd->flags); | 230 | nd->flags); |
| 230 | 231 | /* | |
| 231 | /* If it's our master or we shouldn't trigger a mount we're done */ | 232 | * For an expire of a covered direct or offset mount we need |
| 232 | lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); | 233 | * to beeak out of follow_down() at the autofs mount trigger |
| 233 | if (oz_mode || | 234 | * (d_mounted--), so we can see the expiring flag, and manage |
| 234 | !(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) | 235 | * the blocking and following here until the expire is completed. |
| 236 | */ | ||
| 237 | if (oz_mode) { | ||
| 238 | spin_lock(&sbi->fs_lock); | ||
| 239 | if (ino->flags & AUTOFS_INF_EXPIRING) { | ||
| 240 | spin_unlock(&sbi->fs_lock); | ||
| 241 | /* Follow down to our covering mount. */ | ||
| 242 | if (!follow_down(&nd->path.mnt, &nd->path.dentry)) | ||
| 243 | goto done; | ||
| 244 | /* | ||
| 245 | * We shouldn't need to do this but we have no way | ||
| 246 | * of knowing what may have been done so try a follow | ||
| 247 | * just in case. | ||
| 248 | */ | ||
| 249 | autofs4_follow_mount(&nd->path.mnt, &nd->path.dentry); | ||
| 250 | goto done; | ||
| 251 | } | ||
| 252 | spin_unlock(&sbi->fs_lock); | ||
| 235 | goto done; | 253 | goto done; |
| 254 | } | ||
| 236 | 255 | ||
| 237 | /* If an expire request is pending wait for it. */ | 256 | /* If an expire request is pending everyone must wait. */ |
| 238 | spin_lock(&sbi->fs_lock); | 257 | spin_lock(&sbi->fs_lock); |
| 239 | if (ino->flags & AUTOFS_INF_EXPIRING) { | 258 | if (ino->flags & AUTOFS_INF_EXPIRING) { |
| 240 | spin_unlock(&sbi->fs_lock); | 259 | spin_unlock(&sbi->fs_lock); |
| @@ -243,6 +262,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
| 243 | dentry, dentry->d_name.len, dentry->d_name.name); | 262 | dentry, dentry->d_name.len, dentry->d_name.name); |
| 244 | 263 | ||
| 245 | status = autofs4_wait(sbi, dentry, NFY_NONE); | 264 | status = autofs4_wait(sbi, dentry, NFY_NONE); |
| 265 | wait_for_completion(&ino->expire_complete); | ||
| 246 | 266 | ||
| 247 | DPRINTK("request done status=%d", status); | 267 | DPRINTK("request done status=%d", status); |
| 248 | 268 | ||
| @@ -250,10 +270,15 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
| 250 | } | 270 | } |
| 251 | spin_unlock(&sbi->fs_lock); | 271 | spin_unlock(&sbi->fs_lock); |
| 252 | cont: | 272 | cont: |
| 273 | /* We trigger a mount for almost all flags */ | ||
| 274 | lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); | ||
| 275 | if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) | ||
| 276 | goto done; | ||
| 277 | |||
| 253 | /* | 278 | /* |
| 254 | * If the dentry contains directories then it is an | 279 | * If the dentry contains directories then it is an autofs |
| 255 | * autofs multi-mount with no root mount offset. So | 280 | * multi-mount with no root mount offset. So don't try to |
| 256 | * don't try to mount it again. | 281 | * mount it again. |
| 257 | */ | 282 | */ |
| 258 | spin_lock(&dcache_lock); | 283 | spin_lock(&dcache_lock); |
| 259 | if (dentry->d_flags & DCACHE_AUTOFS_PENDING || | 284 | if (dentry->d_flags & DCACHE_AUTOFS_PENDING || |
| @@ -264,22 +289,22 @@ cont: | |||
| 264 | if (status) | 289 | if (status) |
| 265 | goto out_error; | 290 | goto out_error; |
| 266 | 291 | ||
| 267 | /* | 292 | goto follow; |
| 268 | * The mount succeeded but if there is no root mount | ||
| 269 | * it must be an autofs multi-mount with no root offset | ||
| 270 | * so we don't need to follow the mount. | ||
| 271 | */ | ||
| 272 | if (d_mountpoint(dentry)) { | ||
| 273 | if (!autofs4_follow_mount(&nd->path.mnt, | ||
| 274 | &nd->path.dentry)) { | ||
| 275 | status = -ENOENT; | ||
| 276 | goto out_error; | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | goto done; | ||
| 281 | } | 293 | } |
| 282 | spin_unlock(&dcache_lock); | 294 | spin_unlock(&dcache_lock); |
| 295 | follow: | ||
| 296 | /* | ||
| 297 | * If there is no root mount it must be an autofs | ||
| 298 | * multi-mount with no root offset so we don't need | ||
| 299 | * to follow it. | ||
| 300 | */ | ||
| 301 | if (d_mountpoint(dentry)) { | ||
| 302 | if (!autofs4_follow_mount(&nd->path.mnt, | ||
| 303 | &nd->path.dentry)) { | ||
| 304 | status = -ENOENT; | ||
| 305 | goto out_error; | ||
| 306 | } | ||
| 307 | } | ||
| 283 | 308 | ||
| 284 | done: | 309 | done: |
| 285 | return NULL; | 310 | return NULL; |
| @@ -545,6 +570,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
| 545 | expiring, expiring->d_name.len, | 570 | expiring, expiring->d_name.len, |
| 546 | expiring->d_name.name); | 571 | expiring->d_name.name); |
| 547 | autofs4_wait(sbi, expiring, NFY_NONE); | 572 | autofs4_wait(sbi, expiring, NFY_NONE); |
| 573 | wait_for_completion(&ino->expire_complete); | ||
| 548 | DPRINTK("request completed"); | 574 | DPRINTK("request completed"); |
| 549 | goto cont; | 575 | goto cont; |
| 550 | } | 576 | } |
