struct path     root;
        struct inode    *inode; /* path.dentry.d_inode */
        unsigned int    flags;
-       unsigned        seq, m_seq;
+       unsigned        seq, m_seq, r_seq;
        int             last_type;
        unsigned        depth;
        int             total_link_count;
        if (type == LAST_DOTDOT) {
                int error = 0;
 
-               /*
-                * Scoped-lookup flags resolving ".." is not currently safe --
-                * races can cause our parent to have moved outside of the root
-                * and us to skip over it.
-                */
-               if (unlikely(nd->flags & LOOKUP_IS_SCOPED))
-                       return -EXDEV;
                if (!nd->root.mnt) {
                        error = set_root(nd);
                        if (error)
                                return error;
                }
-               if (nd->flags & LOOKUP_RCU) {
-                       return follow_dotdot_rcu(nd);
-               } else
-                       return follow_dotdot(nd);
+               if (nd->flags & LOOKUP_RCU)
+                       error = follow_dotdot_rcu(nd);
+               else
+                       error = follow_dotdot(nd);
+               if (error)
+                       return error;
+
+               if (unlikely(nd->flags & LOOKUP_IS_SCOPED)) {
+                       /*
+                        * If there was a racing rename or mount along our
+                        * path, then we can't be sure that ".." hasn't jumped
+                        * above nd->root (and so userspace should retry or use
+                        * some fallback).
+                        */
+                       smp_rmb();
+                       if (unlikely(__read_seqcount_retry(&mount_lock.seqcount, nd->m_seq)))
+                               return -EAGAIN;
+                       if (unlikely(__read_seqcount_retry(&rename_lock.seqcount, nd->r_seq)))
+                               return -EAGAIN;
+               }
        }
        return 0;
 }
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT;
        nd->depth = 0;
+
+       nd->m_seq = __read_seqcount_begin(&mount_lock.seqcount);
+       nd->r_seq = __read_seqcount_begin(&rename_lock.seqcount);
+       smp_rmb();
+
        if (flags & LOOKUP_ROOT) {
                struct dentry *root = nd->root.dentry;
                struct inode *inode = root->d_inode;
                nd->path = nd->root;
                nd->inode = inode;
                if (flags & LOOKUP_RCU) {
-                       nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
+                       nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
                        nd->root_seq = nd->seq;
-                       nd->m_seq = read_seqbegin(&mount_lock);
                } else {
                        path_get(&nd->path);
                }
        nd->path.mnt = NULL;
        nd->path.dentry = NULL;
 
-       nd->m_seq = read_seqbegin(&mount_lock);
-
        /* Absolute pathname -- fetch the root (LOOKUP_IN_ROOT uses nd->dfd). */
        if (*s == '/' && !(flags & LOOKUP_IN_ROOT)) {
                error = nd_jump_root(nd);