Merge branch 'work.autofs' into for-linus
authorAl Viro <viro@zeniv.linux.org.uk>
Fri, 16 Dec 2016 21:34:52 +0000 (16:34 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 16 Dec 2016 21:34:52 +0000 (16:34 -0500)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
13 files changed:
Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
fs/autofs4/autofs_i.h
fs/autofs4/dev-ioctl.c
fs/autofs4/expire.c
fs/autofs4/root.c
fs/autofs4/waitq.c
fs/dcache.c
fs/mount.h
fs/namei.c
fs/namespace.c
include/linux/dcache.h
include/linux/mount.h

index 1b5f156..4ca3a8d 100644 (file)
@@ -20,7 +20,7 @@ prototypes:
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
        struct vfsmount *(*d_automount)(struct path *path);
-       int (*d_manage)(struct dentry *, bool);
+       int (*d_manage)(const struct path *, bool);
        struct dentry *(*d_real)(struct dentry *, const struct inode *,
                                 unsigned int);
 
index b5039a0..3893f4d 100644 (file)
@@ -948,7 +948,7 @@ struct dentry_operations {
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
        struct vfsmount *(*d_automount)(struct path *);
-       int (*d_manage)(struct dentry *, bool);
+       int (*d_manage)(const struct path *, bool);
        struct dentry *(*d_real)(struct dentry *, const struct inode *,
                                 unsigned int);
 };
index a1fba42..c885daa 100644 (file)
@@ -145,7 +145,7 @@ void autofs4_free_ino(struct autofs_info *);
 
 /* Expiration */
 int is_autofs4_dentry(struct dentry *);
-int autofs4_expire_wait(struct dentry *dentry, int rcu_walk);
+int autofs4_expire_wait(const struct path *path, int rcu_walk);
 int autofs4_expire_run(struct super_block *, struct vfsmount *,
                       struct autofs_sb_info *,
                       struct autofs_packet_expire __user *);
@@ -217,7 +217,8 @@ static inline int autofs_prepare_pipe(struct file *pipe)
 
 /* Queue management functions */
 
-int autofs4_wait(struct autofs_sb_info *, struct dentry *, enum autofs_notify);
+int autofs4_wait(struct autofs_sb_info *,
+                const struct path *, enum autofs_notify);
 int autofs4_wait_release(struct autofs_sb_info *, autofs_wqt_t, int);
 void autofs4_catatonic_mode(struct autofs_sb_info *);
 
index dfc6f49..6f48d67 100644 (file)
@@ -468,7 +468,7 @@ static int autofs_dev_ioctl_requester(struct file *fp,
        ino = autofs4_dentry_ino(path.dentry);
        if (ino) {
                err = 0;
-               autofs4_expire_wait(path.dentry, 0);
+               autofs4_expire_wait(&path, 0);
                spin_lock(&sbi->fs_lock);
                param->requester.uid =
                        from_kuid_munged(current_user_ns(), ino->uid);
@@ -575,7 +575,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
 
                devid = new_encode_dev(dev);
 
-               err = have_submounts(path.dentry);
+               err = path_has_submounts(&path);
 
                if (follow_down_one(&path))
                        magic = path.dentry->d_sb->s_magic;
index d8e6d42..57725d4 100644 (file)
@@ -310,26 +310,29 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
        now = jiffies;
        timeout = sbi->exp_timeout;
 
-       spin_lock(&sbi->fs_lock);
-       ino = autofs4_dentry_ino(root);
-       /* No point expiring a pending mount */
-       if (ino->flags & AUTOFS_INF_PENDING)
-               goto out;
        if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
+               spin_lock(&sbi->fs_lock);
+               ino = autofs4_dentry_ino(root);
+               /* No point expiring a pending mount */
+               if (ino->flags & AUTOFS_INF_PENDING) {
+                       spin_unlock(&sbi->fs_lock);
+                       goto out;
+               }
                ino->flags |= AUTOFS_INF_WANT_EXPIRE;
                spin_unlock(&sbi->fs_lock);
                synchronize_rcu();
-               spin_lock(&sbi->fs_lock);
                if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
+                       spin_lock(&sbi->fs_lock);
                        ino->flags |= AUTOFS_INF_EXPIRING;
                        init_completion(&ino->expire_complete);
                        spin_unlock(&sbi->fs_lock);
                        return root;
                }
+               spin_lock(&sbi->fs_lock);
                ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
+               spin_unlock(&sbi->fs_lock);
        }
 out:
-       spin_unlock(&sbi->fs_lock);
        dput(root);
 
        return NULL;
@@ -495,8 +498,9 @@ found:
        return expired;
 }
 
-int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
+int autofs4_expire_wait(const struct path *path, int rcu_walk)
 {
+       struct dentry *dentry = path->dentry;
        struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
        struct autofs_info *ino = autofs4_dentry_ino(dentry);
        int status;
@@ -525,7 +529,7 @@ retry:
 
                pr_debug("waiting for expire %p name=%pd\n", dentry, dentry);
 
-               status = autofs4_wait(sbi, dentry, NFY_NONE);
+               status = autofs4_wait(sbi, path, NFY_NONE);
                wait_for_completion(&ino->expire_complete);
 
                pr_debug("expire done status=%d\n", status);
@@ -592,11 +596,12 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
 
        if (dentry) {
                struct autofs_info *ino = autofs4_dentry_ino(dentry);
+               const struct path path = { .mnt = mnt, .dentry = dentry };
 
                /* This is synchronous because it makes the daemon a
                 * little easier
                 */
-               ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
+               ret = autofs4_wait(sbi, &path, NFY_EXPIRE);
 
                spin_lock(&sbi->fs_lock);
                /* avoid rapid-fire expire attempts if expiry fails */
index a11f731..82e8f6e 100644 (file)
@@ -32,7 +32,7 @@ static int autofs4_dir_open(struct inode *inode, struct file *file);
 static struct dentry *autofs4_lookup(struct inode *,
                                     struct dentry *, unsigned int);
 static struct vfsmount *autofs4_d_automount(struct path *);
-static int autofs4_d_manage(struct dentry *, bool);
+static int autofs4_d_manage(const struct path *, bool);
 static void autofs4_dentry_release(struct dentry *);
 
 const struct file_operations autofs4_root_operations = {
@@ -123,7 +123,7 @@ static int autofs4_dir_open(struct inode *inode, struct file *file)
         * it.
         */
        spin_lock(&sbi->lookup_lock);
-       if (!d_mountpoint(dentry) && simple_empty(dentry)) {
+       if (!path_is_mountpoint(&file->f_path) && simple_empty(dentry)) {
                spin_unlock(&sbi->lookup_lock);
                return -ENOENT;
        }
@@ -269,39 +269,41 @@ next:
        return NULL;
 }
 
-static int autofs4_mount_wait(struct dentry *dentry, bool rcu_walk)
+static int autofs4_mount_wait(const struct path *path, bool rcu_walk)
 {
-       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
-       struct autofs_info *ino = autofs4_dentry_ino(dentry);
+       struct autofs_sb_info *sbi = autofs4_sbi(path->dentry->d_sb);
+       struct autofs_info *ino = autofs4_dentry_ino(path->dentry);
        int status = 0;
 
        if (ino->flags & AUTOFS_INF_PENDING) {
                if (rcu_walk)
                        return -ECHILD;
-               pr_debug("waiting for mount name=%pd\n", dentry);
-               status = autofs4_wait(sbi, dentry, NFY_MOUNT);
+               pr_debug("waiting for mount name=%pd\n", path->dentry);
+               status = autofs4_wait(sbi, path, NFY_MOUNT);
                pr_debug("mount wait done status=%d\n", status);
        }
        ino->last_used = jiffies;
        return status;
 }
 
-static int do_expire_wait(struct dentry *dentry, bool rcu_walk)
+static int do_expire_wait(const struct path *path, bool rcu_walk)
 {
+       struct dentry *dentry = path->dentry;
        struct dentry *expiring;
 
        expiring = autofs4_lookup_expiring(dentry, rcu_walk);
        if (IS_ERR(expiring))
                return PTR_ERR(expiring);
        if (!expiring)
-               return autofs4_expire_wait(dentry, rcu_walk);
+               return autofs4_expire_wait(path, rcu_walk);
        else {
+               const struct path this = { .mnt = path->mnt, .dentry = expiring };
                /*
                 * If we are racing with expire the request might not
                 * be quite complete, but the directory has been removed
                 * so it must have been successful, just wait for it.
                 */
-               autofs4_expire_wait(expiring, 0);
+               autofs4_expire_wait(&this, 0);
                autofs4_del_expiring(expiring);
                dput(expiring);
        }
@@ -354,7 +356,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
         * and the directory was removed, so just go ahead and try
         * the mount.
         */
-       status = do_expire_wait(dentry, 0);
+       status = do_expire_wait(path, 0);
        if (status && status != -EAGAIN)
                return NULL;
 
@@ -362,7 +364,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
        spin_lock(&sbi->fs_lock);
        if (ino->flags & AUTOFS_INF_PENDING) {
                spin_unlock(&sbi->fs_lock);
-               status = autofs4_mount_wait(dentry, 0);
+               status = autofs4_mount_wait(path, 0);
                if (status)
                        return ERR_PTR(status);
                goto done;
@@ -370,28 +372,28 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
 
        /*
         * If the dentry is a symlink it's equivalent to a directory
-        * having d_mountpoint() true, so there's no need to call back
-        * to the daemon.
+        * having path_is_mountpoint() true, so there's no need to call
+        * back to the daemon.
         */
        if (d_really_is_positive(dentry) && d_is_symlink(dentry)) {
                spin_unlock(&sbi->fs_lock);
                goto done;
        }
 
-       if (!d_mountpoint(dentry)) {
+       if (!path_is_mountpoint(path)) {
                /*
                 * It's possible that user space hasn't removed directories
                 * after umounting a rootless multi-mount, although it
-                * should. For v5 have_submounts() is sufficient to handle
-                * this because the leaves of the directory tree under the
-                * mount never trigger mounts themselves (they have an autofs
-                * trigger mount mounted on them). But v4 pseudo direct mounts
-                * do need the leaves to trigger mounts. In this case we
-                * have no choice but to use the list_empty() check and
+                * should. For v5 path_has_submounts() is sufficient to
+                * handle this because the leaves of the directory tree under
+                * the mount never trigger mounts themselves (they have an
+                * autofs trigger mount mounted on them). But v4 pseudo direct
+                * mounts do need the leaves to trigger mounts. In this case
+                * we have no choice but to use the list_empty() check and
                 * require user space behave.
                 */
                if (sbi->version > 4) {
-                       if (have_submounts(dentry)) {
+                       if (path_has_submounts(path)) {
                                spin_unlock(&sbi->fs_lock);
                                goto done;
                        }
@@ -403,7 +405,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
                }
                ino->flags |= AUTOFS_INF_PENDING;
                spin_unlock(&sbi->fs_lock);
-               status = autofs4_mount_wait(dentry, 0);
+               status = autofs4_mount_wait(path, 0);
                spin_lock(&sbi->fs_lock);
                ino->flags &= ~AUTOFS_INF_PENDING;
                if (status) {
@@ -421,8 +423,9 @@ done:
        return NULL;
 }
 
-static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
+static int autofs4_d_manage(const struct path *path, bool rcu_walk)
 {
+       struct dentry *dentry = path->dentry;
        struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
        struct autofs_info *ino = autofs4_dentry_ino(dentry);
        int status;
@@ -431,20 +434,20 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
 
        /* The daemon never waits. */
        if (autofs4_oz_mode(sbi)) {
-               if (!d_mountpoint(dentry))
+               if (!path_is_mountpoint(path))
                        return -EISDIR;
                return 0;
        }
 
        /* Wait for pending expires */
-       if (do_expire_wait(dentry, rcu_walk) == -ECHILD)
+       if (do_expire_wait(path, rcu_walk) == -ECHILD)
                return -ECHILD;
 
        /*
         * This dentry may be under construction so wait on mount
         * completion.
         */
-       status = autofs4_mount_wait(dentry, rcu_walk);
+       status = autofs4_mount_wait(path, rcu_walk);
        if (status)
                return status;
 
@@ -460,7 +463,7 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
 
                if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
                        return 0;
-               if (d_mountpoint(dentry))
+               if (path_is_mountpoint(path))
                        return 0;
                inode = d_inode_rcu(dentry);
                if (inode && S_ISLNK(inode->i_mode))
@@ -487,7 +490,7 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
                 * we can avoid needless calls ->d_automount() and avoid
                 * an incorrect ELOOP error return.
                 */
-               if ((!d_mountpoint(dentry) && !simple_empty(dentry)) ||
+               if ((!path_is_mountpoint(path) && !simple_empty(dentry)) ||
                    (d_really_is_positive(dentry) && d_is_symlink(dentry)))
                        status = -EISDIR;
        }
index e44271d..1278335 100644 (file)
@@ -250,8 +250,9 @@ autofs4_find_wait(struct autofs_sb_info *sbi, const struct qstr *qstr)
 static int validate_request(struct autofs_wait_queue **wait,
                            struct autofs_sb_info *sbi,
                            const struct qstr *qstr,
-                           struct dentry *dentry, enum autofs_notify notify)
+                           const struct path *path, enum autofs_notify notify)
 {
+       struct dentry *dentry = path->dentry;
        struct autofs_wait_queue *wq;
        struct autofs_info *ino;
 
@@ -314,6 +315,7 @@ static int validate_request(struct autofs_wait_queue **wait,
         */
        if (notify == NFY_MOUNT) {
                struct dentry *new = NULL;
+               struct path this;
                int valid = 1;
 
                /*
@@ -333,7 +335,9 @@ static int validate_request(struct autofs_wait_queue **wait,
                                        dentry = new;
                        }
                }
-               if (have_submounts(dentry))
+               this.mnt = path->mnt;
+               this.dentry = dentry;
+               if (path_has_submounts(&this))
                        valid = 0;
 
                if (new)
@@ -345,8 +349,9 @@ static int validate_request(struct autofs_wait_queue **wait,
 }
 
 int autofs4_wait(struct autofs_sb_info *sbi,
-                struct dentry *dentry, enum autofs_notify notify)
+                const struct path *path, enum autofs_notify notify)
 {
+       struct dentry *dentry = path->dentry;
        struct autofs_wait_queue *wq;
        struct qstr qstr;
        char *name;
@@ -405,7 +410,7 @@ int autofs4_wait(struct autofs_sb_info *sbi,
                return -EINTR;
        }
 
-       ret = validate_request(&wq, sbi, &qstr, dentry, notify);
+       ret = validate_request(&wq, sbi, &qstr, path, notify);
        if (ret <= 0) {
                if (ret != -EINTR)
                        mutex_unlock(&sbi->wq_mutex);
index 5c7cc95..2523783 100644 (file)
@@ -1273,38 +1273,44 @@ rename_retry:
        goto again;
 }
 
-/*
- * Search for at least 1 mount point in the dentry's subdirs.
- * We descend to the next level whenever the d_subdirs
- * list is non-empty and continue searching.
- */
+struct check_mount {
+       struct vfsmount *mnt;
+       unsigned int mounted;
+};
 
-static enum d_walk_ret check_mount(void *data, struct dentry *dentry)
+static enum d_walk_ret path_check_mount(void *data, struct dentry *dentry)
 {
-       int *ret = data;
-       if (d_mountpoint(dentry)) {
-               *ret = 1;
+       struct check_mount *info = data;
+       struct path path = { .mnt = info->mnt, .dentry = dentry };
+
+       if (likely(!d_mountpoint(dentry)))
+               return D_WALK_CONTINUE;
+       if (__path_is_mountpoint(&path)) {
+               info->mounted = 1;
                return D_WALK_QUIT;
        }
        return D_WALK_CONTINUE;
 }
 
 /**
- * have_submounts - check for mounts over a dentry
- * @parent: dentry to check.
+ * path_has_submounts - check for mounts over a dentry in the
+ *                      current namespace.
+ * @parent: path to check.
  *
  * Return true if the parent or its subdirectories contain
- * a mount point
+ * a mount point in the current namespace.
  */
-int have_submounts(struct dentry *parent)
+int path_has_submounts(const struct path *parent)
 {
-       int ret = 0;
+       struct check_mount data = { .mnt = parent->mnt, .mounted = 0 };
 
-       d_walk(parent, &ret, check_mount, NULL);
+       read_seqlock_excl(&mount_lock);
+       d_walk(parent->dentry, &data, path_check_mount, NULL);
+       read_sequnlock_excl(&mount_lock);
 
-       return ret;
+       return data.mounted;
 }
-EXPORT_SYMBOL(have_submounts);
+EXPORT_SYMBOL(path_has_submounts);
 
 /*
  * Called by mount code to set a mountpoint and check if the mountpoint is
index d2e25d7..2c856fc 100644 (file)
@@ -94,6 +94,12 @@ extern struct mount *__lookup_mnt_last(struct vfsmount *, struct dentry *);
 extern int __legitimize_mnt(struct vfsmount *, unsigned);
 extern bool legitimize_mnt(struct vfsmount *, unsigned);
 
+static inline bool __path_is_mountpoint(const struct path *path)
+{
+       struct mount *m = __lookup_mnt(path->mnt, path->dentry);
+       return m && likely(!(m->mnt.mnt_flags & MNT_SYNC_UMOUNT));
+}
+
 extern void __detach_mounts(struct dentry *dentry);
 
 static inline void detach_mounts(struct dentry *dentry)
index 1c8f438..47781b0 100644 (file)
@@ -1200,7 +1200,7 @@ static int follow_managed(struct path *path, struct nameidata *nd)
                if (managed & DCACHE_MANAGE_TRANSIT) {
                        BUG_ON(!path->dentry->d_op);
                        BUG_ON(!path->dentry->d_op->d_manage);
-                       ret = path->dentry->d_op->d_manage(path->dentry, false);
+                       ret = path->dentry->d_op->d_manage(path, false);
                        if (ret < 0)
                                break;
                }
@@ -1263,10 +1263,10 @@ int follow_down_one(struct path *path)
 }
 EXPORT_SYMBOL(follow_down_one);
 
-static inline int managed_dentry_rcu(struct dentry *dentry)
+static inline int managed_dentry_rcu(const struct path *path)
 {
-       return (dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
-               dentry->d_op->d_manage(dentry, true) : 0;
+       return (path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
+               path->dentry->d_op->d_manage(path, true) : 0;
 }
 
 /*
@@ -1282,7 +1282,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                 * Don't forget we might have a non-mountpoint managed dentry
                 * that wants to block transit.
                 */
-               switch (managed_dentry_rcu(path->dentry)) {
+               switch (managed_dentry_rcu(path)) {
                case -ECHILD:
                default:
                        return false;
@@ -1392,8 +1392,7 @@ int follow_down(struct path *path)
                if (managed & DCACHE_MANAGE_TRANSIT) {
                        BUG_ON(!path->dentry->d_op);
                        BUG_ON(!path->dentry->d_op->d_manage);
-                       ret = path->dentry->d_op->d_manage(
-                               path->dentry, false);
+                       ret = path->dentry->d_op->d_manage(path, false);
                        if (ret < 0)
                                return ret == -EISDIR ? 0 : ret;
                }
index 9ad88a4..f7e28f8 100644 (file)
@@ -1159,6 +1159,35 @@ struct vfsmount *mntget(struct vfsmount *mnt)
 }
 EXPORT_SYMBOL(mntget);
 
+/* path_is_mountpoint() - Check if path is a mount in the current
+ *                          namespace.
+ *
+ *  d_mountpoint() can only be used reliably to establish if a dentry is
+ *  not mounted in any namespace and that common case is handled inline.
+ *  d_mountpoint() isn't aware of the possibility there may be multiple
+ *  mounts using a given dentry in a different namespace. This function
+ *  checks if the passed in path is a mountpoint rather than the dentry
+ *  alone.
+ */
+bool path_is_mountpoint(const struct path *path)
+{
+       unsigned seq;
+       bool res;
+
+       if (!d_mountpoint(path->dentry))
+               return false;
+
+       rcu_read_lock();
+       do {
+               seq = read_seqbegin(&mount_lock);
+               res = __path_is_mountpoint(path);
+       } while (read_seqretry(&mount_lock, seq));
+       rcu_read_unlock();
+
+       return res;
+}
+EXPORT_SYMBOL(path_is_mountpoint);
+
 struct vfsmount *mnt_clone_internal(const struct path *path)
 {
        struct mount *p;
index 5beed7b..c965e44 100644 (file)
@@ -139,7 +139,7 @@ struct dentry_operations {
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
        struct vfsmount *(*d_automount)(struct path *);
-       int (*d_manage)(struct dentry *, bool);
+       int (*d_manage)(const struct path *, bool);
        struct dentry *(*d_real)(struct dentry *, const struct inode *,
                                 unsigned int);
 } ____cacheline_aligned;
@@ -254,7 +254,7 @@ extern struct dentry *d_find_alias(struct inode *);
 extern void d_prune_aliases(struct inode *);
 
 /* test whether we have any submounts in a subdir tree */
-extern int have_submounts(struct dentry *);
+extern int path_has_submounts(const struct path *);
 
 /*
  * This adds the entry to the hash queues.
index cf2b578..c6f5515 100644 (file)
@@ -98,4 +98,6 @@ extern dev_t name_to_dev_t(const char *name);
 
 extern unsigned int sysctl_mount_max;
 
+extern bool path_is_mountpoint(const struct path *path);
+
 #endif /* _LINUX_MOUNT_H */