RCU'd vfsmounts
[platform/adaptation/renesas_rcar/renesas_kernel.git] / fs / namei.c
index 409a441..cb0ebae 100644 (file)
@@ -484,14 +484,12 @@ EXPORT_SYMBOL(path_put);
 
 static inline void lock_rcu_walk(void)
 {
-       br_read_lock(&vfsmount_lock);
        rcu_read_lock();
 }
 
 static inline void unlock_rcu_walk(void)
 {
        rcu_read_unlock();
-       br_read_unlock(&vfsmount_lock);
 }
 
 /**
@@ -512,26 +510,23 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
        BUG_ON(!(nd->flags & LOOKUP_RCU));
 
        /*
-        * Get a reference to the parent first: we're
-        * going to make "path_put(nd->path)" valid in
-        * non-RCU context for "terminate_walk()".
-        *
-        * If this doesn't work, return immediately with
-        * RCU walking still active (and then we will do
-        * the RCU walk cleanup in terminate_walk()).
+        * After legitimizing the bastards, terminate_walk()
+        * will do the right thing for non-RCU mode, and all our
+        * subsequent exit cases should rcu_read_unlock()
+        * before returning.  Do vfsmount first; if dentry
+        * can't be legitimized, just set nd->path.dentry to NULL
+        * and rely on dput(NULL) being a no-op.
         */
-       if (!lockref_get_not_dead(&parent->d_lockref))
+       if (!legitimize_mnt(nd->path.mnt, nd->m_seq))
                return -ECHILD;
-
-       /*
-        * After the mntget(), we terminate_walk() will do
-        * the right thing for non-RCU mode, and all our
-        * subsequent exit cases should unlock_rcu_walk()
-        * before returning.
-        */
-       mntget(nd->path.mnt);
        nd->flags &= ~LOOKUP_RCU;
 
+       if (!lockref_get_not_dead(&parent->d_lockref)) {
+               nd->path.dentry = NULL; 
+               unlock_rcu_walk();
+               return -ECHILD;
+       }
+
        /*
         * For a negative lookup, the lookup sequence point is the parents
         * sequence point, and it only needs to revalidate the parent dentry.
@@ -608,16 +603,21 @@ static int complete_walk(struct nameidata *nd)
                if (!(nd->flags & LOOKUP_ROOT))
                        nd->root.mnt = NULL;
 
+               if (!legitimize_mnt(nd->path.mnt, nd->m_seq)) {
+                       unlock_rcu_walk();
+                       return -ECHILD;
+               }
                if (unlikely(!lockref_get_not_dead(&dentry->d_lockref))) {
                        unlock_rcu_walk();
+                       mntput(nd->path.mnt);
                        return -ECHILD;
                }
                if (read_seqcount_retry(&dentry->d_seq, nd->seq)) {
                        unlock_rcu_walk();
                        dput(dentry);
+                       mntput(nd->path.mnt);
                        return -ECHILD;
                }
-               mntget(nd->path.mnt);
                unlock_rcu_walk();
        }
 
@@ -660,29 +660,6 @@ static __always_inline void set_root_rcu(struct nameidata *nd)
        }
 }
 
-static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
-{
-       int ret;
-
-       if (IS_ERR(link))
-               goto fail;
-
-       if (*link == '/') {
-               set_root(nd);
-               path_put(&nd->path);
-               nd->path = nd->root;
-               path_get(&nd->root);
-               nd->flags |= LOOKUP_JUMPED;
-       }
-       nd->inode = nd->path.dentry->d_inode;
-
-       ret = link_path_walk(link, nd);
-       return ret;
-fail:
-       path_put(&nd->path);
-       return PTR_ERR(link);
-}
-
 static void path_put_conditional(struct path *path, struct nameidata *nd)
 {
        dput(path->dentry);
@@ -874,7 +851,20 @@ follow_link(struct path *link, struct nameidata *nd, void **p)
        error = 0;
        s = nd_get_link(nd);
        if (s) {
-               error = __vfs_follow_link(nd, s);
+               if (unlikely(IS_ERR(s))) {
+                       path_put(&nd->path);
+                       put_link(nd, link, *p);
+                       return PTR_ERR(s);
+               }
+               if (*s == '/') {
+                       set_root(nd);
+                       path_put(&nd->path);
+                       nd->path = nd->root;
+                       path_get(&nd->root);
+                       nd->flags |= LOOKUP_JUMPED;
+               }
+               nd->inode = nd->path.dentry->d_inode;
+               error = link_path_walk(s, nd);
                if (unlikely(error))
                        put_link(nd, link, *p);
        }
@@ -919,15 +909,15 @@ int follow_up(struct path *path)
        struct mount *parent;
        struct dentry *mountpoint;
 
-       br_read_lock(&vfsmount_lock);
+       read_seqlock_excl(&mount_lock);
        parent = mnt->mnt_parent;
        if (parent == mnt) {
-               br_read_unlock(&vfsmount_lock);
+               read_sequnlock_excl(&mount_lock);
                return 0;
        }
        mntget(&parent->mnt);
        mountpoint = dget(mnt->mnt_mountpoint);
-       br_read_unlock(&vfsmount_lock);
+       read_sequnlock_excl(&mount_lock);
        dput(path->dentry);
        path->dentry = mountpoint;
        mntput(path->mnt);
@@ -1058,8 +1048,8 @@ static int follow_managed(struct path *path, unsigned flags)
 
                        /* Something is mounted on this dentry in another
                         * namespace and/or whatever was mounted there in this
-                        * namespace got unmounted before we managed to get the
-                        * vfsmount_lock */
+                        * namespace got unmounted before lookup_mnt() could
+                        * get it */
                }
 
                /* Handle an automount point */
@@ -1121,7 +1111,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                if (!d_mountpoint(path->dentry))
                        break;
 
-               mounted = __lookup_mnt(path->mnt, path->dentry, 1);
+               mounted = __lookup_mnt(path->mnt, path->dentry);
                if (!mounted)
                        break;
                path->mnt = &mounted->mnt;
@@ -1142,7 +1132,7 @@ static void follow_mount_rcu(struct nameidata *nd)
 {
        while (d_mountpoint(nd->path.dentry)) {
                struct mount *mounted;
-               mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry, 1);
+               mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry);
                if (!mounted)
                        break;
                nd->path.mnt = &mounted->mnt;
@@ -1874,6 +1864,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
                if (flags & LOOKUP_RCU) {
                        lock_rcu_walk();
                        nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
+                       nd->m_seq = read_seqbegin(&mount_lock);
                } else {
                        path_get(&nd->path);
                }
@@ -1882,6 +1873,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
 
        nd->root.mnt = NULL;
 
+       nd->m_seq = read_seqbegin(&mount_lock);
        if (*name=='/') {
                if (flags & LOOKUP_RCU) {
                        lock_rcu_walk();
@@ -2271,12 +2263,15 @@ mountpoint_last(struct nameidata *nd, struct path *path)
                dentry = d_alloc(dir, &nd->last);
                if (!dentry) {
                        error = -ENOMEM;
+                       mutex_unlock(&dir->d_inode->i_mutex);
                        goto out;
                }
                dentry = lookup_real(dir->d_inode, dentry, nd->flags);
                error = PTR_ERR(dentry);
-               if (IS_ERR(dentry))
+               if (IS_ERR(dentry)) {
+                       mutex_unlock(&dir->d_inode->i_mutex);
                        goto out;
+               }
        }
        mutex_unlock(&dir->d_inode->i_mutex);
 
@@ -2663,6 +2658,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
        int acc_mode;
        int create_error = 0;
        struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
+       bool excl;
 
        BUG_ON(dentry->d_inode);
 
@@ -2676,10 +2672,9 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
        if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
                mode &= ~current_umask();
 
-       if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) {
+       excl = (open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT);
+       if (excl)
                open_flag &= ~O_TRUNC;
-               *opened |= FILE_CREATED;
-       }
 
        /*
         * Checking write permission is tricky, bacuse we don't know if we are
@@ -2732,12 +2727,6 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
                goto out;
        }
 
-       acc_mode = op->acc_mode;
-       if (*opened & FILE_CREATED) {
-               fsnotify_create(dir, dentry);
-               acc_mode = MAY_OPEN;
-       }
-
        if (error) {    /* returned 1, that is */
                if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
                        error = -EIO;
@@ -2747,9 +2736,19 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
                        dput(dentry);
                        dentry = file->f_path.dentry;
                }
-               if (create_error && dentry->d_inode == NULL) {
-                       error = create_error;
-                       goto out;
+               if (*opened & FILE_CREATED)
+                       fsnotify_create(dir, dentry);
+               if (!dentry->d_inode) {
+                       WARN_ON(*opened & FILE_CREATED);
+                       if (create_error) {
+                               error = create_error;
+                               goto out;
+                       }
+               } else {
+                       if (excl && !(*opened & FILE_CREATED)) {
+                               error = -EEXIST;
+                               goto out;
+                       }
                }
                goto looked_up;
        }
@@ -2758,6 +2757,12 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
         * We didn't have the inode before the open, so check open permission
         * here.
         */
+       acc_mode = op->acc_mode;
+       if (*opened & FILE_CREATED) {
+               WARN_ON(!(open_flag & O_CREAT));
+               fsnotify_create(dir, dentry);
+               acc_mode = MAY_OPEN;
+       }
        error = may_open(&file->f_path, acc_mode, open_flag);
        if (error)
                fput(file);
@@ -4236,11 +4241,6 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
        return res;
 }
 
-int vfs_follow_link(struct nameidata *nd, const char *link)
-{
-       return __vfs_follow_link(nd, link);
-}
-
 /* get the link contents into pagecache */
 static char *page_getlink(struct dentry * dentry, struct page **ppage)
 {
@@ -4352,7 +4352,6 @@ EXPORT_SYMBOL(vfs_path_lookup);
 EXPORT_SYMBOL(inode_permission);
 EXPORT_SYMBOL(unlock_rename);
 EXPORT_SYMBOL(vfs_create);
-EXPORT_SYMBOL(vfs_follow_link);
 EXPORT_SYMBOL(vfs_link);
 EXPORT_SYMBOL(vfs_mkdir);
 EXPORT_SYMBOL(vfs_mknod);