From: David Howells <dhowells@redhat.com>
To: Al Viro <viro@ZenIV.linux.org.uk>
Cc: dhowells@redhat.com, raven@themaw.net, npiggin@kernel.dk,
autofs@linux.kernel.org, linux-fsdevel@vger.kernel.org
Subject: [PATCH 19/18] Unexport do_add_mount() and add in follow_automount(), not ->d_automount()
Date: Fri, 14 Jan 2011 17:26:09 +0000 [thread overview]
Message-ID: <6672.1295025969@redhat.com> (raw)
In-Reply-To: <20110114154652.GD19804@ZenIV.linux.org.uk>
Unexport do_add_mount() and make ->d_automount() return the vfsmount to be
added rather than calling do_add_mount() itself. follow_automount() will then
do the addition.
This slightly complicates things as ->d_automount() normally wants to add the
new vfsmount to an expiration list and start an expiration timer. The problem
with that is that the vfsmount will be deleted if it has a refcount of 1 and
the timer will not repeat if the expiration list is empty.
To this end, we require the vfsmount to be returned from d_automount() with a
refcount of (at least) 2. One of these refs will be dropped unconditionally.
In addition, follow_automount() must get a 3rd ref around the call to
do_add_mount() lest it eat a ref and return an error, leaving the mount we
have open to being expired as we would otherwise have only 1 ref on it. This
would mean the currently upstream code is buggy for AFS, CIFS and NFS.
d_automount() should also add the the vfsmount to the expiration list (by
calling mnt_set_expiry()) and start the expiration timer before returning, if
this mechanism is to be used. The vfsmount will be unlinked from the
expiration list by follow_automount() if do_add_mount() fails.
This patch also fixes the call to do_add_mount() for AFS and CIFS to propagate
the mount flags from the parent vfsmount.
Signed-off-by: David Howells <dhowells@redhat.com>
---
Documentation/filesystems/vfs.txt | 23 ++++++++++++--------
fs/afs/mntpt.c | 25 +++++-----------------
fs/cifs/cifs_dfs_ref.c | 26 +++++------------------
fs/internal.h | 2 ++
fs/namei.c | 42 +++++++++++++++++++++++++++++++------
fs/namespace.c | 41 +++++++++++++++++++++++++++++-------
fs/nfs/namespace.c | 24 ++++-----------------
include/linux/mount.h | 7 +-----
8 files changed, 101 insertions(+), 89 deletions(-)
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index 3c4b2f1..94cf97b 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -933,15 +933,20 @@ struct dentry_operations {
dynamic_dname() helper function is provided to take care of this.
d_automount: called when an automount dentry is to be traversed (optional).
- This should create a new VFS mount record, mount it on the directory
- and return the record to the caller. The caller is supplied with a
- path parameter giving the automount directory to describe the automount
- target and the parent VFS mount record to provide inheritable mount
- parameters. NULL should be returned if someone else managed to make
- the automount first. If the automount failed, then an error code
- should be returned. If -EISDIR is returned, then the directory will
- be treated as an ordinary directory and returned to pathwalk to
- continue walking.
+ This should create a new VFS mount record and return the record to the
+ caller. The caller is supplied with a path parameter giving the
+ automount directory to describe the automount target and the parent
+ VFS mount record to provide inheritable mount parameters. NULL should
+ be returned if someone else managed to make the automount first. If
+ the vfsmount creation failed, then an error code should be returned.
+ If -EISDIR is returned, then the directory will be treated as an
+ ordinary directory and returned to pathwalk to continue walking.
+
+ If a vfsmount is returned, the caller will attempt to mount it on the
+ mountpoint and will remove the vfsmount from its expiration list in
+ the case of failure. The vfsmount should be returned with 2 refs on
+ it to prevent automatic expiration - the caller will clean up the
+ additional ref.
This function is only used if DCACHE_NEED_AUTOMOUNT is set on the
dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c
index 0f7dd7a..0d74c2c 100644
--- a/fs/afs/mntpt.c
+++ b/fs/afs/mntpt.c
@@ -241,7 +241,6 @@ error_no_devname:
struct vfsmount *afs_d_automount(struct path *path)
{
struct vfsmount *newmnt;
- int err;
_enter("{%s,%s}", path->mnt->mnt_devname, path->dentry->d_name.name);
@@ -249,24 +248,12 @@ struct vfsmount *afs_d_automount(struct path *path)
if (IS_ERR(newmnt))
return newmnt;
- mntget(newmnt);
- err = do_add_mount(newmnt, path, MNT_SHRINKABLE, &afs_vfsmounts);
- switch (err) {
- case 0:
- schedule_delayed_work(&afs_mntpt_expiry_timer,
- afs_mntpt_expiry_timeout * HZ);
- _leave(" = %p {%s}", newmnt, newmnt->mnt_devname);
- return newmnt;
- case -EBUSY:
- /* someone else made a mount here whilst we were busy */
- mntput(newmnt);
- _leave(" = NULL [EBUSY]");
- return NULL;
- default:
- mntput(newmnt);
- _leave(" = %d", err);
- return ERR_PTR(err);
- }
+ mntget(newmnt); /* prevent immediate expiration */
+ mnt_set_expiry(newmnt, &afs_vfsmounts);
+ schedule_delayed_work(&afs_mntpt_expiry_timer,
+ afs_mntpt_expiry_timeout * HZ);
+ _leave(" = %p {%s}", newmnt, newmnt->mnt_devname);
+ return newmnt;
}
/*
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index ddd0b3e..7ed3653 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -351,7 +351,6 @@ free_xid:
struct vfsmount *cifs_dfs_d_automount(struct path *path)
{
struct vfsmount *newmnt;
- int err;
cFYI(1, "in %s", __func__);
@@ -361,25 +360,12 @@ struct vfsmount *cifs_dfs_d_automount(struct path *path)
return newmnt;
}
- mntget(newmnt);
- err = do_add_mount(newmnt, path, MNT_SHRINKABLE,
- &cifs_dfs_automount_list);
- switch (err) {
- case 0:
- schedule_delayed_work(&cifs_dfs_automount_task,
- cifs_dfs_mountpoint_expiry_timeout);
- cFYI(1, "leaving %s [ok]" , __func__);
- return newmnt;
- case -EBUSY:
- /* someone else made a mount here whilst we were busy */
- mntput(newmnt);
- cFYI(1, "leaving %s [EBUSY]" , __func__);
- return NULL;
- default:
- mntput(newmnt);
- cFYI(1, "leaving %s [error %d]" , __func__, err);
- return ERR_PTR(err);
- }
+ mntget(newmnt); /* prevent immediate expiration */
+ mnt_set_expiry(newmnt, &cifs_dfs_automount_list);
+ schedule_delayed_work(&cifs_dfs_automount_task,
+ cifs_dfs_mountpoint_expiry_timeout);
+ cFYI(1, "leaving %s [ok]" , __func__);
+ return newmnt;
}
const struct inode_operations cifs_dfs_referral_inode_operations = {
diff --git a/fs/internal.h b/fs/internal.h
index 9687c2e..4931060 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -70,6 +70,8 @@ extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *,
extern void release_mounts(struct list_head *);
extern void umount_tree(struct vfsmount *, int, struct list_head *);
extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
+extern int do_add_mount(struct vfsmount *, struct path *, int);
+extern void mnt_clear_expiry(struct vfsmount *);
extern void __init mnt_init(void);
diff --git a/fs/namei.c b/fs/namei.c
index b099541..cd7b7e4 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -898,6 +898,7 @@ static int follow_automount(struct path *path, unsigned flags,
bool *need_mntput)
{
struct vfsmount *mnt;
+ int err;
if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
return -EREMOTE;
@@ -940,22 +941,49 @@ static int follow_automount(struct path *path, unsigned flags,
return -EREMOTE;
return PTR_ERR(mnt);
}
+
if (!mnt) /* mount collision */
return 0;
+ /* The new mount record should have at least 2 refs to prevent it being
+ * expired before we get a chance to add it
+ */
+ BUG_ON(mnt_get_count(mnt) < 2);
+
if (mnt->mnt_sb == path->mnt->mnt_sb &&
mnt->mnt_root == path->dentry) {
+ mnt_clear_expiry(mnt);
+ mntput(mnt);
mntput(mnt);
return -ELOOP;
}
- dput(path->dentry);
- if (*need_mntput)
- mntput(path->mnt);
- path->mnt = mnt;
- path->dentry = dget(mnt->mnt_root);
- *need_mntput = true;
- return 0;
+ /* We need to add the mountpoint to the parent. The filesystem may
+ * have placed it on an expiry list, and so we need to make sure it
+ * won't be expired under us if do_add_mount() fails (do_add_mount()
+ * will eat a reference unconditionally).
+ */
+ mntget(mnt);
+ err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
+ switch (err) {
+ case -EBUSY:
+ /* Someone else made a mount here whilst we were busy */
+ err = 0;
+ default:
+ mnt_clear_expiry(mnt);
+ mntput(mnt);
+ mntput(mnt);
+ return err;
+ case 0:
+ mntput(mnt);
+ dput(path->dentry);
+ if (*need_mntput)
+ mntput(path->mnt);
+ path->mnt = mnt;
+ path->dentry = dget(mnt->mnt_root);
+ *need_mntput = true;
+ return 0;
+ }
}
/*
diff --git a/fs/namespace.c b/fs/namespace.c
index d94ccd6..bfcb701 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1925,15 +1925,14 @@ static int do_new_mount(struct path *path, char *type, int flags,
if (IS_ERR(mnt))
return PTR_ERR(mnt);
- return do_add_mount(mnt, path, mnt_flags, NULL);
+ return do_add_mount(mnt, path, mnt_flags);
}
/*
* add a mount into a namespace's mount tree
- * - provide the option of adding the new mount to an expiration list
+ * - this unconditionally eats one of the caller's references to newmnt.
*/
-int do_add_mount(struct vfsmount *newmnt, struct path *path,
- int mnt_flags, struct list_head *fslist)
+int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
{
int err;
@@ -1963,9 +1962,6 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
if ((err = graft_tree(newmnt, path)))
goto unlock;
- if (fslist) /* add to the specified expiration list */
- list_add_tail(&newmnt->mnt_expire, fslist);
-
up_write(&namespace_sem);
return 0;
@@ -1975,7 +1971,36 @@ unlock:
return err;
}
-EXPORT_SYMBOL_GPL(do_add_mount);
+/**
+ * mnt_set_expiry - Put a mount on an expiration list
+ * @mnt: The mount to list.
+ * @expiry_list: The list to add the mount to.
+ */
+void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list)
+{
+ down_write(&namespace_sem);
+ br_write_lock(vfsmount_lock);
+
+ list_add_tail(&mnt->mnt_expire, expiry_list);
+
+ br_write_unlock(vfsmount_lock);
+ up_write(&namespace_sem);
+}
+EXPORT_SYMBOL(mnt_set_expiry);
+
+/*
+ * Remove a vfsmount from any expiration list it may be on
+ */
+void mnt_clear_expiry(struct vfsmount *mnt)
+{
+ if (!list_empty(&mnt->mnt_expire)) {
+ down_write(&namespace_sem);
+ br_write_lock(vfsmount_lock);
+ list_del_init(&mnt->mnt_expire);
+ br_write_unlock(vfsmount_lock);
+ up_write(&namespace_sem);
+ }
+}
/*
* process a list of expirable mountpoints with the intent of discarding any
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index f3fbb1b..f32b860 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -149,26 +149,10 @@ struct vfsmount *nfs_d_automount(struct path *path)
if (IS_ERR(mnt))
goto out;
- mntget(mnt);
- err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE,
- &nfs_automount_list);
- switch (err) {
- case 0:
- dprintk("%s: done, success\n", __func__);
- schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
- break;
- case -EBUSY:
- /* someone else made a mount here whilst we were busy */
- mntput(mnt);
- dprintk("%s: done, collision\n", __func__);
- mnt = NULL;
- break;
- default:
- mntput(mnt);
- dprintk("%s: done, error %d\n", __func__, err);
- mnt = ERR_PTR(err);
- break;
- }
+ dprintk("%s: done, success\n", __func__);
+ mntget(mnt); /* prevent immediate expiration */
+ mnt_set_expiry(mnt, &nfs_automount_list);
+ schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
out:
nfs_free_fattr(fattr);
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 1869ea2..af4765e 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -110,12 +110,7 @@ extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
int flags, const char *name,
void *data);
-struct nameidata;
-
-struct path;
-extern int do_add_mount(struct vfsmount *newmnt, struct path *path,
- int mnt_flags, struct list_head *fslist);
-
+extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
extern void mark_mounts_for_expiry(struct list_head *mounts);
extern dev_t name_to_dev_t(char *name);
next prev parent reply other threads:[~2011-01-14 17:26 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-01-13 21:53 [PATCH 00/18] Introduce automount support in the VFS [ver #4] David Howells
2011-01-13 21:54 ` [PATCH 01/18] Add a dentry op to handle automounting rather than abusing follow_link() " David Howells
2011-01-16 0:09 ` Al Viro
2011-01-16 1:17 ` Al Viro
2011-01-16 18:12 ` David Howells
2011-01-13 21:54 ` [PATCH 02/18] Add a dentry op to allow processes to be held during pathwalk transit " David Howells
2011-01-13 21:54 ` [PATCH 03/18] From: David Howells <dhowells@redhat.com> " David Howells
2011-01-13 21:54 ` [PATCH 04/18] AFS: Use d_automount() rather than abusing follow_link() " David Howells
2011-01-13 21:54 ` [PATCH 05/18] NFS: " David Howells
2011-01-13 21:54 ` [PATCH 06/18] CIFS: " David Howells
2011-01-13 21:54 ` [PATCH 07/18] Remove the automount through follow_link() kludge code from pathwalk " David Howells
2011-01-13 21:54 ` [PATCH 08/18] autofs4: Add d_automount() dentry operation " David Howells
2011-01-13 21:54 ` [PATCH 09/18] autofs4: Add d_manage() " David Howells
2011-01-14 13:51 ` Ian Kent
2011-01-14 14:37 ` Nick Piggin
2011-01-14 15:35 ` David Howells
2011-01-14 15:46 ` Nick Piggin
2011-01-14 15:47 ` Nick Piggin
2011-01-13 21:54 ` [PATCH 10/18] autofs4: Remove unused code " David Howells
2011-01-13 21:54 ` [PATCH 11/18] autofs4: Clean up inode operations " David Howells
2011-01-13 21:55 ` [PATCH 12/18] autofs4: Clean up dentry " David Howells
2011-01-13 21:55 ` [PATCH 13/18] autofs4: Clean up autofs4_free_ino() " David Howells
2011-01-14 16:03 ` Al Viro
2011-01-13 21:55 ` [PATCH 14/18] autofs4: Fix wait validation " David Howells
2011-01-13 21:55 ` [PATCH 15/18] autofs4: Add v4 pseudo direct mount support " David Howells
2011-01-13 21:55 ` [PATCH 16/18] autofs4: Bump version " David Howells
2011-01-13 21:55 ` [PATCH 17/18] Remove a further kludge from __do_follow_link() " David Howells
2011-01-13 21:55 ` [PATCH 18/18] Allow d_manage() to be used in RCU-walk mode " David Howells
2011-01-14 7:02 ` [PATCH 00/18] Introduce automount support in the VFS " Al Viro
2011-01-14 7:05 ` Al Viro
2011-01-14 11:20 ` David Howells
2011-01-14 11:43 ` David Howells
2011-01-14 11:54 ` David Howells
2011-01-14 11:54 ` David Howells
2011-01-14 15:46 ` Al Viro
2011-01-14 17:26 ` David Howells [this message]
2011-01-14 17:30 ` [PATCH 19/18] Unexport do_add_mount() and add in follow_automount(), not ->d_automount() David Howells
2011-01-14 17:43 ` Al Viro
2011-01-14 17:56 ` Al Viro
2011-01-14 18:06 ` Al Viro
2011-01-14 22:07 ` Nick Piggin
2011-01-15 13:30 ` Al Viro
2011-01-15 18:33 ` Nick Piggin
2011-01-16 0:24 ` Al Viro
2011-01-16 1:21 ` Nick Piggin
2011-01-15 18:46 ` Nick Piggin
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=6672.1295025969@redhat.com \
--to=dhowells@redhat.com \
--cc=autofs@linux.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=npiggin@kernel.dk \
--cc=raven@themaw.net \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.