[PATCH RFC v2] vfs: make fstatat retry on ESTALE errors from getattr call

From: Jeff Layton
Date: Mon Apr 16 2012 - 12:56:05 EST


Here's another RFC patch that incorporates some of the suggestions so
far:

1) it converts the "goto" construct into a do-while loop

2) This patch drops the FS_* flag so all filesystems would be affected.

3) It adds a backoff timeout mechanism to the loop with an arbitrary 5s cap
on the sleep between attempts (this is certainly negotiable).

4) Also, if the process catches a fatal signal, then we'd break out of the
loop and return the ESTALE to userspace. Not that it would care at that
point since the program is going to die anyway.

Clearly this inlined function will need to go to a header and the cap on
the sleep is probably negotiable. This is just a proof of concept thing
at this point.

Also note that this patch still doesn't incorporate any hard cap on the
number of retries. I could add that too, but perhaps that's not
necessary since we're handling the fatal_signal case here?

Opinions welcome...

Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
---
fs/stat.c | 35 +++++++++++++++++++++++++++++------
1 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/fs/stat.c b/fs/stat.c
index c733dc5..71f2f5b 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -68,12 +68,25 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
}
EXPORT_SYMBOL(vfs_fstat);

+/* max retry timeout of 5s */
+#define MAX_ESTALE_RETRY_TIMEOUT (5 * HZ)
+
+static inline bool
+estale_retry(int error, long timeout)
+{
+ if (likely(error != -ESTALE))
+ return false;
+
+ return !schedule_timeout_killable(timeout);
+}
+
int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
int flag)
{
struct path path;
int error = -EINVAL;
- int lookup_flags = 0;
+ long timeout = 1;
+ unsigned int lookup_flags = 0;

if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
AT_EMPTY_PATH)) != 0)
@@ -84,12 +97,22 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
if (flag & AT_EMPTY_PATH)
lookup_flags |= LOOKUP_EMPTY;

- error = user_path_at(dfd, filename, lookup_flags, &path);
- if (error)
- goto out;
+ do {
+ /*
+ * if lookup fails with ESTALE, then that implies that the
+ * root of the mount went stale. Give up at that point...
+ */
+ error = user_path_at(dfd, filename, lookup_flags, &path);
+ if (error)
+ break;

- error = vfs_getattr(path.mnt, path.dentry, stat);
- path_put(&path);
+ error = vfs_getattr(path.mnt, path.dentry, stat);
+ path_put(&path);
+ lookup_flags |= LOOKUP_REVAL;
+ timeout <<= 1;
+ if (timeout > MAX_ESTALE_RETRY_TIMEOUT)
+ timeout = MAX_ESTALE_RETRY_TIMEOUT;
+ } while (estale_retry(error, timeout));
out:
return error;
}
--
1.7.7.6

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/