linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "J. Bruce Fields" <bfields-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
To: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Christoph Hellwig <hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>,
	Al Viro <viro-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>,
	linux-nfs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	"J. Bruce Fields"
	<bfields-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Subject: [PATCH 5/5] exportfs: fix quadratic behavior in filehandle lookup
Date: Tue, 15 Oct 2013 16:39:33 -0400	[thread overview]
Message-ID: <1381869574-10662-6-git-send-email-bfields@redhat.com> (raw)
In-Reply-To: <1381869574-10662-1-git-send-email-bfields-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

From: "J. Bruce Fields" <bfields-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

We shouldn't really have to re-lookup the parents on each pass through.
This should make behavior linear instead of quadratic as a function of
directory depth.

Signed-off-by: J. Bruce Fields <bfields-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 fs/exportfs/expfs.c |  105 +++++++++++++++++++--------------------------------
 1 file changed, 39 insertions(+), 66 deletions(-)

diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index afa7c9c..08544ae 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -69,11 +69,7 @@ find_acceptable_alias(struct dentry *result,
 	return NULL;
 }
 
-/*
- * Find root of a disconnected subtree and return a reference to it.
- */
-static struct dentry *
-find_disconnected_root(struct dentry *dentry)
+static bool dentry_connected(struct dentry *dentry)
 {
 	dget(dentry);
 	while (!IS_ROOT(dentry)) {
@@ -81,13 +77,14 @@ find_disconnected_root(struct dentry *dentry)
 
 		if (!(parent->d_flags & DCACHE_DISCONNECTED)) {
 			dput(parent);
-			break;
+			dput(dentry);
+			return true;
 		}
 
 		dput(dentry);
 		dentry = parent;
 	}
-	return dentry;
+	return false;
 }
 
 static void clear_disconnected(struct dentry *dentry)
@@ -109,11 +106,13 @@ static void clear_disconnected(struct dentry *dentry)
 /*
  * Return the parent directory on success.
  *
- * Return NULL to keep trying.
+ * If it races with a rename or unlink, assume the other operation
+ * connected it, but return NULL to indicate the caller should check
+ * this just to make sure the filesystem isn't nuts.
  *
  * Otherwise return an error.
  */
-static int reconnect_one(struct vfsmount *mnt, struct dentry *dentry, char *nbuf, int *noprogress)
+static struct dentry *reconnect_one(struct vfsmount *mnt, struct dentry *dentry, char *nbuf)
 {
 	struct dentry *parent;
 	struct dentry *tmp;
@@ -137,20 +136,17 @@ static int reconnect_one(struct vfsmount *mnt, struct dentry *dentry, char *nbuf
 		err = PTR_ERR(parent);
 		dprintk("%s: get_parent of %ld failed, err %d\n",
 			__func__, dentry->d_inode->i_ino, err);
-		return err;
+		return parent;
 	}
 
 	dprintk("%s: find name of %lu in %lu\n", __func__,
 		dentry->d_inode->i_ino, parent->d_inode->i_ino);
 	err = exportfs_get_name(mnt, parent, nbuf, dentry);
 	if (err) {
-		dput(parent);
 		if (err == -ENOENT)
-			/* some race between get_parent and
-			 * get_name?  just try again
-			 */
-			return 0;
-		return err;
+			 return NULL;
+		dput(parent);
+		return ERR_PTR(err);
 	}
 	dprintk("%s: found name: %s\n", __func__, nbuf);
 	mutex_lock(&parent->d_inode->i_mutex);
@@ -160,26 +156,13 @@ static int reconnect_one(struct vfsmount *mnt, struct dentry *dentry, char *nbuf
 		err = PTR_ERR(tmp);
 		dprintk("%s: lookup failed: %d\n",
 			__func__, err);
-		dput(parent);
-		return err;
+		return NULL;
 	}
-	/* we didn't really want tmp, we really wanted
-	 * a side-effect of the lookup.
-	 * hopefully, tmp == dentry, though it isn't really
-	 * a problem if it isn't
-	 */
-	if (tmp == dentry)
-		*noprogress = 0;
-	else
-		printk("%s: tmp != dentry\n", __func__);
+	/* Note tmp != dentry is possible at this point, but that's OK */
 	dput(tmp);
-	dput(parent);
-	if (IS_ROOT(dentry)) {
-		/* something went wrong, we have to give up */
-		dput(dentry);
-		return -ESTALE;
-	}
-	return 0;
+	if (IS_ROOT(dentry))
+		return ERR_PTR(-ESTALE);
+	return parent;
 }
 
 /*
@@ -190,51 +173,41 @@ static int reconnect_one(struct vfsmount *mnt, struct dentry *dentry, char *nbuf
 static int
 reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
 {
-	int noprogress = 0;
-	int err = -ESTALE;
+	struct dentry *dentry, *parent;
 
-	/*
-	 * It is possible that a confused file system might not let us complete
-	 * the path to the root.  For example, if get_parent returns a directory
-	 * in which we cannot find a name for the child.  While this implies a
-	 * very sick filesystem we don't want it to cause knfsd to spin.  Hence
-	 * the noprogress counter.  If we go through the loop 10 times (2 is
-	 * probably enough) without getting anywhere, we just give up
-	 */
-	while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) {
-		struct dentry *dentry = find_disconnected_root(target_dir);
+	dentry = dget(target_dir);
 
-		if (!IS_ROOT(dentry)) {
-			/* must have found a connected parent - great */
-			clear_disconnected(target_dir);
-			noprogress = 0;
-			dput(dentry);
-			continue;
-		}
+	while (dentry->d_flags & DCACHE_DISCONNECTED) {
 		if (dentry == mnt->mnt_sb->s_root) {
-			printk(KERN_ERR "export: Eeek filesystem root is not connected, impossible\n");
-			clear_disconnected(target_dir);
-			noprogress = 0;
+			WARN_ON_ONCE(1);
+			break;
+		}
+		if (!IS_ROOT(dentry)) {
+			parent = dget_parent(dentry);
 			dput(dentry);
+			dentry = parent;
 			continue;
 		}
 		/*
 		 * We have hit the top of a disconnected path, try to
 		 * find parent and connect.
 		 */
-		err = reconnect_one(mnt, dentry, nbuf, &noprogress);
-		if (err)
-			return err;
+		parent = reconnect_one(mnt, dentry, nbuf);
 		dput(dentry);
+		if (!parent)
+			break;
+		if (IS_ERR(parent))
+			return PTR_ERR(parent);
+		dentry = parent;
 	}
-
-	if (target_dir->d_flags & DCACHE_DISCONNECTED) {
-		/* something went wrong - oh-well */
-		if (!err)
-			err = -ESTALE;
-		return err;
+	/*
+	 * Should be unnecessary, but let's be careful:
+	 */
+	if (!dentry_connected(target_dir)) {
+		WARN_ON_ONCE(1);
+		return -ESTALE;
 	}
-
+	clear_disconnected(target_dir);
 	return 0;
 }
 
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

  parent reply	other threads:[~2013-10-15 20:39 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-10-15 20:39 simplify reconnecting dentries looked up by filehandle J. Bruce Fields
2013-10-15 20:39 ` [PATCH 1/5] exportfs: clear DISCONNECTED on all parents sooner J. Bruce Fields
     [not found]   ` <1381869574-10662-2-git-send-email-bfields-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-10-16  7:13     ` Christoph Hellwig
     [not found]       ` <20131016071343.GB27799-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
2013-10-16 13:56         ` J. Bruce Fields
2013-10-15 20:39 ` [PATCH 2/5] exportfs: move most of reconnect_path to helper function J. Bruce Fields
     [not found]   ` <1381869574-10662-3-git-send-email-bfields-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-10-16  7:16     ` Christoph Hellwig
     [not found] ` <1381869574-10662-1-git-send-email-bfields-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-10-15 20:39   ` [PATCH 3/5] exportfs: make variable names more helpful J. Bruce Fields
     [not found]     ` <1381869574-10662-4-git-send-email-bfields-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-10-16  7:18       ` Christoph Hellwig
2013-10-15 20:39   ` J. Bruce Fields [this message]
     [not found]     ` <1381869574-10662-6-git-send-email-bfields-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-10-16  8:04       ` [PATCH 5/5] exportfs: fix quadratic behavior in filehandle lookup Christoph Hellwig
2013-10-16 18:29         ` J. Bruce Fields
2013-10-16 19:22         ` J. Bruce Fields
2013-10-16 22:00         ` J. Bruce Fields
2013-10-16 18:24   ` simplify reconnecting dentries looked up by filehandle Christoph Hellwig
2013-10-15 20:39 ` [PATCH 4/5] exportfs: slight reorganization of reconnect loop J. Bruce Fields
2013-10-16  7:19   ` Christoph Hellwig

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=1381869574-10662-6-git-send-email-bfields@redhat.com \
    --to=bfields-h+wxahxf7alqt0dzr+alfa@public.gmane.org \
    --cc=hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org \
    --cc=linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-nfs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=viro-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).