follow_dotdot{,_rcu}(): lift switching nd->path to parent out of loop
authorAl Viro <viro@zeniv.linux.org.uk>
Wed, 26 Feb 2020 16:55:02 +0000 (11:55 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Sat, 14 Mar 2020 01:09:13 +0000 (21:09 -0400)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/namei.c

index 88e5d79..772d82d 100644 (file)
@@ -1365,7 +1365,9 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry,
 
 static int follow_dotdot_rcu(struct nameidata *nd)
 {
+       struct dentry *parent = NULL;
        struct inode *inode = nd->inode;
+       unsigned seq;
 
        while (1) {
                if (path_equal(&nd->path, &nd->root)) {
@@ -1375,15 +1377,12 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                }
                if (nd->path.dentry != nd->path.mnt->mnt_root) {
                        struct dentry *old = nd->path.dentry;
-                       struct dentry *parent = old->d_parent;
-                       unsigned seq;
 
+                       parent = old->d_parent;
                        inode = parent->d_inode;
                        seq = read_seqcount_begin(&parent->d_seq);
                        if (unlikely(read_seqcount_retry(&old->d_seq, nd->seq)))
                                return -ECHILD;
-                       nd->path.dentry = parent;
-                       nd->seq = seq;
                        if (unlikely(!path_connected(nd->path.mnt, parent)))
                                return -ECHILD;
                        break;
@@ -1406,6 +1405,10 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                        nd->seq = seq;
                }
        }
+       if (likely(parent)) {
+               nd->path.dentry = parent;
+               nd->seq = seq;
+       }
        while (unlikely(d_mountpoint(nd->path.dentry))) {
                struct mount *mounted;
                mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry);
@@ -1442,7 +1445,7 @@ static void follow_mount(struct path *path)
 
 static int follow_dotdot(struct nameidata *nd)
 {
-       struct dentry *parent;
+       struct dentry *parent = NULL;
        while (1) {
                if (path_equal(&nd->path, &nd->root)) {
                        if (unlikely(nd->flags & LOOKUP_BENEATH))
@@ -1452,13 +1455,10 @@ static int follow_dotdot(struct nameidata *nd)
                if (nd->path.dentry != nd->path.mnt->mnt_root) {
                        /* rare case of legitimate dget_parent()... */
                        parent = dget_parent(nd->path.dentry);
-
                        if (unlikely(!path_connected(nd->path.mnt, parent))) {
                                dput(parent);
                                return -ENOENT;
                        }
-                       dput(nd->path.dentry);
-                       nd->path.dentry = parent;
                        break;
                }
                if (!follow_up(&nd->path))
@@ -1466,6 +1466,10 @@ static int follow_dotdot(struct nameidata *nd)
                if (unlikely(nd->flags & LOOKUP_NO_XDEV))
                        return -EXDEV;
        }
+       if (likely(parent)) {
+               dput(nd->path.dentry);
+               nd->path.dentry = parent;
+       }
        follow_mount(&nd->path);
        nd->inode = nd->path.dentry->d_inode;
        return 0;