From: Ian Kent <raven@themaw.net>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: autofs mailing list <autofs@linux.kernel.org>,
Kernel Mailing List <linux-kernel@vger.kernel.org>,
linux-fsdevel <linux-fsdevel@vger.kernel.org>,
Al Viro <viro@ZenIV.linux.org.uk>,
Linus Torvalds <torvalds@linux-foundation.org>
Subject: [PATCH 5/7] autofs4 - fix direct mount pending expire race
Date: Fri, 18 Jul 2008 10:37:27 +0800 [thread overview]
Message-ID: <20080718023725.12802.74137.stgit@raven.themaw.net> (raw)
In-Reply-To: <20080718023651.12802.59107.stgit@raven.themaw.net>
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>
---
fs/autofs4/autofs_i.h | 3 ++
fs/autofs4/expire.c | 16 +++++++++--
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 5d90ed3..4b40cbc 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -52,6 +52,8 @@ struct autofs_info {
int flags;
+ struct completion expire_complete;
+
struct list_head active;
struct list_head expiring;
@@ -69,6 +71,7 @@ struct autofs_info {
};
#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */
+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */
struct autofs_wait_queue {
wait_queue_head_t queue;
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 19f5bea..705b9f0 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,
now = jiffies;
timeout = sbi->exp_timeout;
- /* Lock the tree as we must expire as a whole */
spin_lock(&sbi->fs_lock);
if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
struct autofs_info *ino = autofs4_dentry_ino(root);
-
- /* Set this flag early to catch sys_chdir and the like */
+ if (d_mountpoint(root)) {
+ ino->flags |= AUTOFS_INF_MOUNTPOINT;
+ root->d_mounted--;
+ }
ino->flags |= AUTOFS_INF_EXPIRING;
+ init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
return root;
}
@@ -392,6 +394,7 @@ found:
expired, (int)expired->d_name.len, expired->d_name.name);
ino = autofs4_dentry_ino(expired);
ino->flags |= AUTOFS_INF_EXPIRING;
+ init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
spin_lock(&dcache_lock);
list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
@@ -429,6 +432,7 @@ int autofs4_expire_run(struct super_block *sb,
spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(dentry);
ino->flags &= ~AUTOFS_INF_EXPIRING;
+ complete_all(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
return ret;
@@ -457,8 +461,14 @@ int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt,
/* This is synchronous because it makes the daemon a
little easier */
ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
+
spin_lock(&sbi->fs_lock);
+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) {
+ sb->s_root->d_mounted++;
+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
+ }
ino->flags &= ~AUTOFS_INF_EXPIRING;
+ complete_all(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
dput(dentry);
}
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 2ed2a51..bdf190b 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)
dentry, dentry->d_name.len, dentry->d_name.name);
status = autofs4_wait(sbi, dentry, NFY_NONE);
+ wait_for_completion(&ino->expire_complete);
DPRINTK("expire done status=%d", status);
@@ -227,14 +228,32 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d",
dentry, dentry->d_name.len, dentry->d_name.name, oz_mode,
nd->flags);
-
- /* If it's our master or we shouldn't trigger a mount we're done */
- lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS);
- if (oz_mode ||
- !(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING))
+ /*
+ * For an expire of a covered direct or offset mount we need
+ * to beeak out of follow_down() at the autofs mount trigger
+ * (d_mounted--), so we can see the expiring flag, and manage
+ * the blocking and following here until the expire is completed.
+ */
+ if (oz_mode) {
+ spin_lock(&sbi->fs_lock);
+ if (ino->flags & AUTOFS_INF_EXPIRING) {
+ spin_unlock(&sbi->fs_lock);
+ /* Follow down to our covering mount. */
+ if (!follow_down(&nd->path.mnt, &nd->path.dentry))
+ goto done;
+ /*
+ * We shouldn't need to do this but we have no way
+ * of knowing what may have been done so try a follow
+ * just in case.
+ */
+ autofs4_follow_mount(&nd->path.mnt, &nd->path.dentry);
+ goto done;
+ }
+ spin_unlock(&sbi->fs_lock);
goto done;
+ }
- /* If an expire request is pending wait for it. */
+ /* If an expire request is pending everyone must wait. */
spin_lock(&sbi->fs_lock);
if (ino->flags & AUTOFS_INF_EXPIRING) {
spin_unlock(&sbi->fs_lock);
@@ -243,6 +262,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
dentry, dentry->d_name.len, dentry->d_name.name);
status = autofs4_wait(sbi, dentry, NFY_NONE);
+ wait_for_completion(&ino->expire_complete);
DPRINTK("request done status=%d", status);
@@ -250,10 +270,15 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
}
spin_unlock(&sbi->fs_lock);
cont:
+ /* We trigger a mount for almost all flags */
+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS);
+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING))
+ goto done;
+
/*
- * If the dentry contains directories then it is an
- * autofs multi-mount with no root mount offset. So
- * don't try to mount it again.
+ * If the dentry contains directories then it is an autofs
+ * multi-mount with no root mount offset. So don't try to
+ * mount it again.
*/
spin_lock(&dcache_lock);
if (dentry->d_flags & DCACHE_AUTOFS_PENDING ||
@@ -264,22 +289,22 @@ cont:
if (status)
goto out_error;
- /*
- * The mount succeeded but if there is no root mount
- * it must be an autofs multi-mount with no root offset
- * so we don't need to follow the mount.
- */
- if (d_mountpoint(dentry)) {
- if (!autofs4_follow_mount(&nd->path.mnt,
- &nd->path.dentry)) {
- status = -ENOENT;
- goto out_error;
- }
- }
-
- goto done;
+ goto follow;
}
spin_unlock(&dcache_lock);
+follow:
+ /*
+ * If there is no root mount it must be an autofs
+ * multi-mount with no root offset so we don't need
+ * to follow it.
+ */
+ if (d_mountpoint(dentry)) {
+ if (!autofs4_follow_mount(&nd->path.mnt,
+ &nd->path.dentry)) {
+ status = -ENOENT;
+ goto out_error;
+ }
+ }
done:
return NULL;
@@ -545,6 +570,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
expiring, expiring->d_name.len,
expiring->d_name.name);
autofs4_wait(sbi, expiring, NFY_NONE);
+ wait_for_completion(&ino->expire_complete);
DPRINTK("request completed");
goto cont;
}
next prev parent reply other threads:[~2008-07-18 2:42 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-07-18 2:36 [PATCH 1/7] autofs4 - indirect dentry must almost always be positive Ian Kent
2008-07-18 2:37 ` [PATCH 2/7] autofs4 - cleanup redundant readir code Ian Kent
2008-07-18 14:47 ` Jeff Moyer
2008-07-18 15:26 ` Ian Kent
2008-07-18 2:37 ` [PATCH 3/7] autofs4 - fix pending checks Ian Kent
2008-07-18 14:51 ` Jeff Moyer
2008-07-18 2:37 ` [PATCH 4/7] autofs4 - fix indirect mount pending expire race Ian Kent
2008-07-18 18:52 ` Jeff Moyer
2008-07-18 18:56 ` Ian Kent
2008-07-18 19:14 ` Jeff Moyer
2008-07-18 2:37 ` Ian Kent [this message]
2008-07-18 20:08 ` [PATCH 5/7] autofs4 - fix direct " Jeff Moyer
2008-07-19 2:09 ` Ian Kent
2008-07-19 7:21 ` Ian Kent
2008-07-18 2:37 ` [PATCH 6/7] autofs4 - reorganize expire pending wait function calls Ian Kent
2008-07-18 2:37 ` [PATCH 7/7] autofs4 - remove unused ioctls Ian Kent
2008-07-18 20:09 ` Jeff Moyer
2008-07-18 11:56 ` [PATCH 1/7] autofs4 - indirect dentry must almost always be positive Jeff Moyer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080718023725.12802.74137.stgit@raven.themaw.net \
--to=raven@themaw.net \
--cc=akpm@linux-foundation.org \
--cc=autofs@linux.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=torvalds@linux-foundation.org \
--cc=viro@ZenIV.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox