This patch series implements client side support for volatile file handle
recovery (RFC 3530 section 4.2 and 4.3) with walk back using the dcache. To
test the client you either need a server that supports volatile file handles or
you can hard code the Linux server to output NFS4ERR_FHEXPIRED instead of
NFSERR_STALE.
The approach used here for recovery is to perform lookups for each file handle
that receives a FHEXPIRED error. If the lookup also fails with FHEXPIRED, using
the dcache, it will recursively walk back to the root of the mount, and recover
that using get_root.
The 2 key changes in this iteration are fixing recovery from getattr and
adding support for VFH during migration/replication. Recovery from getattr is
accomplished by taking the file handle and searching for a match in the dcache.
I couldn't find a better way to get a dentry or inode from the filehandle,
since nfs_fhget() needs the fattr for the file. (Which clearly isn't accessible
or up to date if getattr is being called)
For migration/replication, if FH4_VOL_MIGRATION is set in FH_EXPIRE_TYPE, then
after the client migrates or replicates to another server we can assume that
all the file handles have expired. nfs4_vfh_replication() will traverse all
the dentries in the dcache for the NFS mount and perform a nfs4_proc_lookup()
on them to refresh the file handle. However, the VFH migration/replication
recovery function is currently not being used. When migration and replication
are added to the tree I will add patches that will call it at the appropriate
time.
Also, when used for replication and migration, volatile file handles allows
the client to switch between 2 servers that have file synced exports
but different filesystems. For example, if 2 read-only servers are synced
using rsync and then remain static the client should have no issues switching
between the servers with volatile file handles.
Volatile file handle support fulfills an actual need. The z/OS NFS server
currently uses volatile file handles and the Linux client can't handle them
preventing those who use the z/OS server from connecting with a Linux client.
Additionally, other clients, such as Solaris, AIX, z/OS, and possibly Windows,
can handle volatile file handles. So I think including support is necessary
despite the flaws in the spec that were previously discussed. This is why I
included a mount option that disables the recovery routines by default, so
there is no risk to the user who doesn't understand what the potential issues
are when recovering from volatile file handles.
v3:
- Fixed recovery from nfs4_proc_getattr() which is only passed a file handle
- Added support for migration/replication file handle recovery
v2:
- Added rpc_cred passing to the vfh lookup and added handling of access errors
on the lookup
Matthew Treinish (10):
New mount option for volatile filehandle recovery
Add support for FH_EXPIRE_TYPE attribute.
Save root file handle in nfs_server.
Store root dentry in server object
Store objects in nfs4_exception to be used during FHEXPIRED recovery.
Add nfs4_vfh_getdentry() for getattr recovery.
Add VFH FHEXPIRED recovery functions.
Perform recovery on both inodes for rename.
Added error handling for NFS4ERR_FHEXPIRED
VFH recovery from a replication/migration event.
fs/nfs/client.c | 3 +
fs/nfs/getroot.c | 8 +
fs/nfs/nfs4_fs.h | 5 +
fs/nfs/nfs4proc.c | 422 +++++++++++++++++++++++++++++++++++++++++----
fs/nfs/nfs4xdr.c | 27 +++
fs/nfs/super.c | 7 +
include/linux/nfs_fs_sb.h | 3 +
include/linux/nfs_mount.h | 1 +
include/linux/nfs_xdr.h | 1 +
9 files changed, 447 insertions(+), 30 deletions(-)
--
1.7.4.4
Added nfs4_vfh_getdentry() that traverses all the dcache
entries for the nfs mount and looks for a matching file handle.
The dentry with a matching file handle can then be used for
recovering from a FHEXPIRED error.
Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/nfs4proc.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 47 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 3706c44..ee62c1e 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2547,6 +2547,53 @@ static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qst
return err;
}
+/* In the case of gettattr only a fh is given, and we need a VFS object to do
+ * vfh recovery. This function traverses the dcache to find the dentry with
+ * a matching fh
+ */
+struct dentry *nfs4_vfh_getdentry(struct nfs_server *server, struct nfs_fh *fhandle)
+{
+ struct dentry *this_parent;
+ struct list_head *next;
+ struct dentry *d_root = server->rootdentry;
+
+ this_parent = d_root;
+repeat:
+ next = this_parent->d_subdirs.next;
+resume:
+ while (next != &this_parent->d_subdirs) {
+ struct list_head *tmp = next;
+ struct dentry *dentry = list_entry(tmp, struct dentry,
+ d_u.d_child);
+ next = tmp->next;
+ /* Compare dentry's fh with one provided */
+ if (dentry && dentry->d_inode && &dentry->d_name)
+ if (!nfs_compare_fh(NFS_FH(dentry->d_inode), fhandle))
+ return dget(dentry);
+
+ if (!list_empty(&dentry->d_subdirs)) {
+ this_parent = dentry;
+ goto repeat;
+ }
+ }
+ if (this_parent != d_root) {
+ struct dentry *child = this_parent;
+ struct dentry *new = this_parent->d_parent;
+ /*
+ * might go back up the wrong parent if we have had a rename
+ * or deletion return ESTALE if it is renamed or deleted
+ */
+ if (new != this_parent->d_parent ||
+ (this_parent->d_flags & DCACHE_DISCONNECTED)) {
+ return ERR_PTR(-ESTALE);
+ }
+ this_parent = new;
+ next = child->d_u.d_child.next;
+ goto resume;
+ }
+ return ERR_PTR(-ENOENT);
+}
+
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
struct nfs_server *server = NFS_SERVER(inode);
--
1.7.4.4
FH_EXPIRE_TYPE is used by the client to determine
what type of file handle the server is providing for a
particular filesystem.
I added the bitmask to fsinfo since the bitmask is
set per filesystem.
Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/client.c | 2 ++
fs/nfs/nfs4proc.c | 3 ++-
fs/nfs/nfs4xdr.c | 27 +++++++++++++++++++++++++++
include/linux/nfs_fs_sb.h | 1 +
include/linux/nfs_xdr.h | 1 +
5 files changed, 33 insertions(+), 1 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 8563585..2f96f9d 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -976,6 +976,8 @@ static void nfs_server_set_fsinfo(struct nfs_server *server,
server->time_delta = fsinfo->time_delta;
+ server->fhexpiretype = fsinfo->fhexpiretype;
+
/* We're airborne Set socket buffersize */
rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 87c584d..ca5e3cd 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -146,7 +146,8 @@ const u32 nfs4_pathconf_bitmap[2] = {
const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE
| FATTR4_WORD0_MAXREAD
| FATTR4_WORD0_MAXWRITE
- | FATTR4_WORD0_LEASE_TIME,
+ | FATTR4_WORD0_LEASE_TIME
+ | FATTR4_WORD0_FH_EXPIRE_TYPE,
FATTR4_WORD1_TIME_DELTA
| FATTR4_WORD1_FS_LAYOUT_TYPES,
FATTR4_WORD2_LAYOUT_BLKSIZE
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index ae78343..7b1f729 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4524,6 +4524,30 @@ static int decode_attr_layout_blksize(struct xdr_stream *xdr, uint32_t *bitmap,
return 0;
}
+/*
+ * The VFH expire type bitmask
+ */
+
+static int decode_attr_fh_expire_type(struct xdr_stream *xdr, uint32_t *bitmap,
+ uint32_t *res)
+{
+ __be32 *p;
+
+ dprintk("%s: bitmap %x\n", __func__, bitmap[0]);
+ *res = 0;
+ if (bitmap[0] & FATTR4_WORD0_FH_EXPIRE_TYPE) {
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(!p)) {
+ print_overflow_msg(__func__, xdr);
+ return -EIO;
+ }
+ *res = be32_to_cpup(p);
+ bitmap[0] &= ~FATTR4_WORD0_FH_EXPIRE_TYPE;
+ }
+ return 0;
+
+}
+
static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
{
__be32 *savep;
@@ -4539,6 +4563,9 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
fsinfo->rtmult = fsinfo->wtmult = 512; /* ??? */
+ if ((status = decode_attr_fh_expire_type(xdr, bitmap, &fsinfo->fhexpiretype)) != 0)
+ goto xdr_error;
+
if ((status = decode_attr_lease_time(xdr, bitmap, &fsinfo->lease_time)) != 0)
goto xdr_error;
if ((status = decode_attr_maxfilesize(xdr, bitmap, &fsinfo->maxfilesize)) != 0)
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 3bf4766..15a2056 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -114,6 +114,7 @@ struct nfs_server {
unsigned int dtsize; /* readdir size */
unsigned short port; /* "port=" setting */
unsigned int bsize; /* server block size */
+ unsigned int fhexpiretype; /* VFH attributes */
unsigned int acregmin; /* attr cache timeouts */
unsigned int acregmax;
unsigned int acdirmin;
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index adbc84a..b96f8d3 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -131,6 +131,7 @@ struct nfs_fsinfo {
__u32 lease_time; /* in seconds */
__u32 layouttype; /* supported pnfs layout driver */
__u32 blksize; /* preferred pnfs io block size */
+ __u32 fhexpiretype; /* VFH attributes */
};
struct nfs_fsstat {
--
1.7.4.4
The new 'vfhretry' mount option will be used to enable the volatile filehandle
recovery routines in the client. On an expired filehandle recover the client
will attempt to recover by performing a lookup on the name of the file.
This option enables volatile filehandle recovery by re-lookup on FHEXPIRED
errors. Only use this mount option if the filenames/paths on the server are not
going to change from the initial expiration until all the recovery operations
complete. Otherwise the validity of the files recovered from the server can not
be guaranteed. It can only truly be considered safe to use this recovery
mechanism on a linux server, if the filesystem is read-only.
This mechanism of recovery isn't necessarily safe for a posix filesystem so
using the mount option will allow the user to enable this at their own risk.
If the mount option is not turned on, the FHEXPIRED error will be converted to
ESTALE.
Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/super.c | 6 ++++++
include/linux/nfs_mount.h | 1 +
2 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 6708f30..6c0be34e 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -87,6 +87,7 @@ enum {
Opt_sharecache, Opt_nosharecache,
Opt_resvport, Opt_noresvport,
Opt_fscache, Opt_nofscache,
+ Opt_vfhretry,
/* Mount options that take integer arguments */
Opt_port,
@@ -151,6 +152,7 @@ static const match_table_t nfs_mount_option_tokens = {
{ Opt_noresvport, "noresvport" },
{ Opt_fscache, "fsc" },
{ Opt_nofscache, "nofsc" },
+ { Opt_vfhretry, "vfhretry" },
{ Opt_port, "port=%s" },
{ Opt_rsize, "rsize=%s" },
@@ -652,6 +654,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
{ NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
{ NFS_MOUNT_UNSHARED, ",nosharecache", "" },
{ NFS_MOUNT_NORESVPORT, ",noresvport", "" },
+ { NFS_MOUNT_VFHRETRY, ",vfhretry", ""},
{ 0, NULL, NULL }
};
const struct proc_nfs_info *nfs_infop;
@@ -1230,6 +1233,9 @@ static int nfs_parse_mount_options(char *raw,
kfree(mnt->fscache_uniq);
mnt->fscache_uniq = NULL;
break;
+ case Opt_vfhretry:
+ mnt->flags |= NFS_MOUNT_VFHRETRY;
+ break;
/*
* options that take numeric values
diff --git a/include/linux/nfs_mount.h b/include/linux/nfs_mount.h
index 576bddd..dba0e23 100644
--- a/include/linux/nfs_mount.h
+++ b/include/linux/nfs_mount.h
@@ -73,5 +73,6 @@ struct nfs_mount_data {
#define NFS_MOUNT_LOCAL_FLOCK 0x100000
#define NFS_MOUNT_LOCAL_FCNTL 0x200000
+#define NFS_MOUNT_VFHRETRY 0x400000
#endif
--
1.7.4.4
Implements a refreshing file handles after a successful migration
or replication event. After the client changes to using a different
server if FH4_VOL_MIGRATION is set by the server it is assumed
that the file handles have all expired during migration or
replication.
nfs4_vfh_replication uses the dcache to find each file handle that
is currently cached and refreshes it using an nfs4 lookup.
Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/nfs4_fs.h | 1 +
fs/nfs/nfs4proc.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 74 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 52d7f94..b4ca67f 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -218,6 +218,7 @@ extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, boo
extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
struct nfs4_fs_locations *fs_locations, struct page *page);
+extern int nfs4_vfh_replication(struct dentry *d_root);
extern void nfs4_release_lockowner(const struct nfs4_lock_state *);
extern const struct xattr_handler *nfs4_xattr_handlers[];
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 78ed5c5..f9484c3 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2558,6 +2558,79 @@ static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qst
return err;
}
+int nfs4_vfh_replication(struct dentry *d_root)
+{
+ struct nfs_fsinfo info;
+ int err = 0;
+ struct nfs_fattr *fattr = NULL;
+ struct dentry *this_parent;
+ struct list_head *next;
+ struct nfs_server *server = NFS_SB(d_root->d_sb);
+ fattr = nfs_alloc_fattr();
+ if (fattr == NULL)
+ return -ENOMEM;
+ info.fattr = fattr;
+ /* Check if we need to refresh filehandles */
+ if ((server->fhexpiretype & NFS4_FH_VOL_MIGRATION
+ || server->fhexpiretype & NFS4_FH_VOLATILE_ANY)
+ && server->flags & NFS_MOUNT_VFHRETRY) {
+ /* Update root dentry */
+ err = nfs4_proc_get_root(server, NFS_FH(d_root->d_inode),
+ &info);
+ if (!err) {
+ if (NFS_FILEID(d_root->d_inode) != info.fattr->fileid)
+ set_nfs_fileid(d_root->d_inode,
+ info.fattr->fileid);
+ nfs_copy_fh(server->rootfh, NFS_FH(d_root->d_inode));
+ /* Only needed if fsid changes on server */
+ memcpy(&server->fsid, &info.fattr->fsid,
+ sizeof(server->fsid));
+ } else
+ return err;
+ /* Loop over dcache entries */
+ this_parent = d_root;
+repeat:
+ next = this_parent->d_subdirs.next;
+resume:
+ while (next != &this_parent->d_subdirs) {
+ struct list_head *tmp = next;
+ struct dentry *dentry = list_entry(tmp, struct dentry,
+ d_u.d_child);
+ next = tmp->next;
+ /* Perform NFS lookup on dentry */
+ if (dentry->d_parent->d_inode && dentry->d_inode &&
+ &dentry->d_name)
+ nfs4_proc_lookup(server->client,
+ dentry->d_parent->d_inode,
+ &dentry->d_name,
+ NFS_FH(dentry->d_inode),
+ fattr);
+ if (!list_empty(&dentry->d_subdirs)) {
+ this_parent = dentry;
+ goto repeat;
+ }
+ }
+ if (this_parent != d_root) {
+ struct dentry *child = this_parent;
+ struct dentry *new = this_parent->d_parent;
+ /*
+ * might go back up the wrong parent if we have had a rename
+ * or deletion return ESTALE if it is renamed or deleted
+ */
+ if (new != this_parent->d_parent ||
+ (this_parent->d_flags & DCACHE_DISCONNECTED)) {
+ return -ESTALE;
+ }
+ this_parent = new;
+ next = child->d_u.d_child.next;
+ goto resume;
+ }
+ return 0;
+
+ } else
+ return 0;
+}
+
/* In the case of gettattr only a fh is given, and we need a VFS object to do
* vfh recovery. This function traverses the dcache to find the dentry with
* a matching fh
--
1.7.4.4
On Mon, Mar 05, 2012 at 03:26:41PM -0500, Matthew Treinish wrote:
> This patch series implements client side support for volatile file handle
> recovery (RFC 3530 section 4.2 and 4.3) with walk back using the dcache. To
> test the client you either need a server that supports volatile file handles or
> you can hard code the Linux server to output NFS4ERR_FHEXPIRED instead of
> NFSERR_STALE.
>
> The approach used here for recovery is to perform lookups for each file handle
> that receives a FHEXPIRED error. If the lookup also fails with FHEXPIRED, using
> the dcache, it will recursively walk back to the root of the mount, and recover
> that using get_root.
>
> The 2 key changes in this iteration are fixing recovery from getattr and
> adding support for VFH during migration/replication. Recovery from getattr is
> accomplished by taking the file handle and searching for a match in the dcache.
> I couldn't find a better way to get a dentry or inode from the filehandle,
> since nfs_fhget() needs the fattr for the file. (Which clearly isn't accessible
> or up to date if getattr is being called)
>
> For migration/replication, if FH4_VOL_MIGRATION is set in FH_EXPIRE_TYPE, then
> after the client migrates or replicates to another server we can assume that
> all the file handles have expired. nfs4_vfh_replication() will traverse all
> the dentries in the dcache for the NFS mount and perform a nfs4_proc_lookup()
> on them to refresh the file handle. However, the VFH migration/replication
> recovery function is currently not being used. When migration and replication
> are added to the tree I will add patches that will call it at the appropriate
> time.
>
> Also, when used for replication and migration, volatile file handles allows
> the client to switch between 2 servers that have file synced exports
> but different filesystems. For example, if 2 read-only servers are synced
> using rsync and then remain static the client should have no issues switching
> between the servers with volatile file handles.
>
> Volatile file handle support fulfills an actual need. The z/OS NFS server
> currently uses volatile file handles and the Linux client can't handle them
> preventing those who use the z/OS server from connecting with a Linux client.
> Additionally, other clients, such as Solaris, AIX, z/OS, and possibly Windows,
> can handle volatile file handles. So I think including support is necessary
> despite the flaws in the spec that were previously discussed. This is why I
> included a mount option that disables the recovery routines by default, so
> there is no risk to the user who doesn't understand what the potential issues
> are when recovering from volatile file handles.
>
> v3:
> - Fixed recovery from nfs4_proc_getattr() which is only passed a file handle
> - Added support for migration/replication file handle recovery
>
> v2:
> - Added rpc_cred passing to the vfh lookup and added handling of access errors
> on the lookup
>
> Matthew Treinish (10):
> New mount option for volatile filehandle recovery
> Add support for FH_EXPIRE_TYPE attribute.
> Save root file handle in nfs_server.
> Store root dentry in server object
> Store objects in nfs4_exception to be used during FHEXPIRED recovery.
> Add nfs4_vfh_getdentry() for getattr recovery.
> Add VFH FHEXPIRED recovery functions.
> Perform recovery on both inodes for rename.
> Added error handling for NFS4ERR_FHEXPIRED
> VFH recovery from a replication/migration event.
>
> fs/nfs/client.c | 3 +
> fs/nfs/getroot.c | 8 +
> fs/nfs/nfs4_fs.h | 5 +
> fs/nfs/nfs4proc.c | 422 +++++++++++++++++++++++++++++++++++++++++----
> fs/nfs/nfs4xdr.c | 27 +++
> fs/nfs/super.c | 7 +
> include/linux/nfs_fs_sb.h | 3 +
> include/linux/nfs_mount.h | 1 +
> include/linux/nfs_xdr.h | 1 +
> 9 files changed, 447 insertions(+), 30 deletions(-)
>
> --
> 1.7.4.4
>
Did anyone get a chance to take a look at these patches? I'd appreciate some
feedback and opinions on it.
Thanks,
Matt Treinish
To successfully recover an expired file handle we'll need either
an inode or preferably a dentry to enable us to recursively walk
back to the root of the export.
v3:
- Add support for getattr recovery by storing the file handle in
nfs4_exception.
v2:
- Added credential storage in the exception object. This will allow
switching credentials on an access error.
Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/nfs4_fs.h | 4 ++
fs/nfs/nfs4proc.c | 122 ++++++++++++++++++++++++++++++++++++++++++----------
2 files changed, 102 insertions(+), 24 deletions(-)
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index b133b50..52d7f94 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -183,6 +183,10 @@ struct nfs4_exception {
long timeout;
int retry;
struct nfs4_state *state;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct nfs_fh *fhandle;
+ struct rpc_cred *cred;
};
struct nfs4_state_recovery_ops {
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index ca5e3cd..3706c44 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1260,7 +1260,10 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(state->inode);
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = state->inode,
+ .dentry = NULL,
+ };
int err;
do {
err = _nfs4_do_open_reclaim(ctx, state);
@@ -1302,7 +1305,10 @@ static int _nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs
int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = state->inode,
+ .dentry = NULL,
+ };
struct nfs_server *server = NFS_SERVER(state->inode);
int err;
do {
@@ -1692,7 +1698,10 @@ static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *s
static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(state->inode);
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = state->inode,
+ .dentry = NULL,
+ };
int err;
do {
@@ -1839,7 +1848,11 @@ out_err:
static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .dentry = dentry,
+ .inode = NULL,
+ .cred = cred,
+ };
struct nfs4_state *res;
int status;
@@ -1930,7 +1943,10 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(inode);
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = inode,
+ .dentry = NULL,
+ };
int err;
do {
err = nfs4_handle_exception(server,
@@ -2400,7 +2416,10 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .fhandle = fhandle,
+ .dentry = NULL,
+ };
int err;
do {
err = nfs4_handle_exception(server,
@@ -2505,7 +2524,10 @@ void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr, struct nfs_fh *fh)
static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = dir,
+ .dentry = NULL,
+ };
int err;
do {
int status;
@@ -2582,7 +2604,11 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = inode,
+ .dentry = NULL,
+ .cred = entry->cred,
+ };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(inode),
@@ -2638,7 +2664,10 @@ static int _nfs4_proc_readlink(struct inode *inode, struct page *page,
static int nfs4_proc_readlink(struct inode *inode, struct page *page,
unsigned int pgbase, unsigned int pglen)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = inode,
+ .dentry = NULL,
+ };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(inode),
@@ -2730,7 +2759,10 @@ out:
static int nfs4_proc_remove(struct inode *dir, struct qstr *name)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = dir,
+ .dentry = NULL,
+ };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
@@ -2885,7 +2917,10 @@ out:
static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = inode,
+ .dentry = NULL,
+ };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(inode),
@@ -2977,7 +3012,10 @@ out:
static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
struct page *page, unsigned int len, struct iattr *sattr)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .dentry = dentry,
+ .inode = NULL,
+ };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
@@ -3008,7 +3046,10 @@ out:
static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
struct iattr *sattr)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .dentry = dentry,
+ .inode = NULL,
+ };
int err;
sattr->ia_mode &= ~current_umask();
@@ -3062,7 +3103,10 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page **pages, unsigned int count, int plus)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .dentry = dentry,
+ .inode = NULL,
+ };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode),
@@ -3110,7 +3154,10 @@ out:
static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
struct iattr *sattr, dev_t rdev)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .dentry = dentry,
+ .inode = NULL,
+ };
int err;
sattr->ia_mode &= ~current_umask();
@@ -3659,7 +3706,10 @@ out_free:
static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = inode,
+ .dentry = NULL,
+ };
ssize_t ret;
do {
ret = __nfs4_get_acl_uncached(inode, buf, buflen);
@@ -3736,7 +3786,10 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = inode,
+ .dentry = NULL,
+ };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(inode),
@@ -4000,7 +4053,10 @@ out:
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync)
{
struct nfs_server *server = NFS_SERVER(inode);
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = inode,
+ .dentry = NULL,
+ };
int err;
do {
err = _nfs4_proc_delegreturn(inode, cred, stateid, issync);
@@ -4074,7 +4130,10 @@ out:
static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = state->inode,
+ .dentry = NULL,
+ };
int err;
do {
@@ -4480,7 +4539,10 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request)
{
struct nfs_server *server = NFS_SERVER(state->inode);
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = state->inode,
+ .dentry = NULL,
+ };
int err;
do {
@@ -4498,7 +4560,10 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request
static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request)
{
struct nfs_server *server = NFS_SERVER(state->inode);
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = state->inode,
+ .dentry = NULL,
+ };
int err;
err = nfs4_set_lock_state(state, request);
@@ -4596,7 +4661,10 @@ out:
static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = state->inode,
+ .dentry = NULL,
+ };
int err;
do {
@@ -4656,7 +4724,10 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
{
struct nfs_server *server = NFS_SERVER(state->inode);
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = state->inode,
+ .dentry = NULL,
+ };
int err;
err = nfs4_set_lock_state(state, fl);
@@ -4863,7 +4934,10 @@ static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct
int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = dir,
+ .dentry = NULL,
+ };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
--
1.7.4.4
Added checks in the nfs4_handle_exception for FHEXPIRED.
If FHEXPIRED is received from the server and the appropriate
attributes are enabled then the client calls
nfs4_fhexpired_recovery() to perform the lookup operation to
try and recovery the expired vfh.
If the mount option is not enabled or the server FH_EXPIRE_TYPE
doesn't have VOLATILE_ANY then client will convert the
FHEXPIRED error into ESTALE since recovery isn't possible.
Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/nfs4proc.c | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index a593eef..78ed5c5 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -283,6 +283,16 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
case -NFS4ERR_STALE_CLIENTID:
nfs4_schedule_lease_recovery(clp);
goto wait_on_recovery;
+ case -NFS4ERR_FHEXPIRED:
+ if (server->flags & NFS_MOUNT_VFHRETRY)
+ if (server->fhexpiretype & NFS4_FH_VOLATILE_ANY) {
+ ret = nfs4_fhexpired_recovery(server, exception);
+ if (!ret)
+ ret = -EAGAIN;
+ break;
+ }
+ ret = -ESTALE;
+ break;
#if defined(CONFIG_NFS_V4_1)
case -NFS4ERR_BADSESSION:
case -NFS4ERR_BADSLOT:
--
1.7.4.4
This is needed for vfh migration/replication recovery and recovering
from FHEXPIRED returned during getattr. It will be used as
a starting point for dcache traversal.
Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/getroot.c | 1 +
fs/nfs/super.c | 1 +
include/linux/nfs_fs_sb.h | 1 +
3 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c
index 51ca63b..8d07346 100644
--- a/fs/nfs/getroot.c
+++ b/fs/nfs/getroot.c
@@ -255,6 +255,7 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh,
dprintk("nfs_get_root: get root dentry failed\n");
goto out;
}
+ server->rootdentry = dget(ret);
security_d_instantiate(ret, inode);
spin_lock(&ret->d_lock);
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 6c0be34e..0b26768 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2892,6 +2892,7 @@ static void nfs4_kill_super(struct super_block *sb)
dprintk("--> %s\n", __func__);
nfs_super_return_all_delegations(sb);
+ dput(server->rootdentry);
kill_anon_super(sb);
nfs_fscache_release_super_cookie(sb);
nfs_free_server(server);
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 5993319..68155b1 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -154,6 +154,7 @@ struct nfs_server {
/* the following fields are protected by nfs_client->cl_lock */
struct rb_root state_owners;
#endif
+ struct dentry *rootdentry;
struct ida openowner_id;
struct ida lockowner_id;
struct list_head state_owners_lru;
--
1.7.4.4
Save each FSID's root directory file handle in the
export's local nfs_server structure on the client.
This FH can then be used as the end case for the recursive
walkback during VFH recovery.
This was originally written by Chuck Lever so that
the FH can be used for migration recovery logic.
Signed-off-by: Chuck Lever <[email protected]>
Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/client.c | 1 +
fs/nfs/getroot.c | 7 +++++++
include/linux/nfs_fs_sb.h | 1 +
3 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 2f96f9d..7566325 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -1137,6 +1137,7 @@ void nfs_free_server(struct nfs_server *server)
ida_destroy(&server->lockowner_id);
ida_destroy(&server->openowner_id);
nfs_free_iostats(server->io_stats);
+ nfs_free_fhandle(server->rootfh);
bdi_destroy(&server->backing_dev_info);
kfree(server);
nfs_release_automount_timer();
diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c
index dcb6154..51ca63b 100644
--- a/fs/nfs/getroot.c
+++ b/fs/nfs/getroot.c
@@ -232,6 +232,13 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh,
ret = ERR_CAST(inode);
goto out;
}
+ server->rootfh = nfs_alloc_fhandle();
+ if (server->rootfh == NULL) {
+ dprintk("nfs_get_root: alloc rootfh failed\n");
+ ret = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ nfs_copy_fh(server->rootfh, mntfh);
error = nfs_superblock_set_dummy_root(sb, inode);
if (error != 0) {
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 15a2056..5993319 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -160,6 +160,7 @@ struct nfs_server {
struct list_head layouts;
struct list_head delegations;
void (*destroy)(struct nfs_server *);
+ struct nfs_fh *rootfh;
atomic_t active; /* Keep trace of any activity to this server */
--
1.7.4.4
The VFH recovery functions perform a recursive walk back on FHEXPIRED
errors. If an FHEXPIRED error is received during a recovery lookup
This means that the directory's filehandle is also expired and
needs to be recovered.
The end case for the recursion is if the file handle is the rootfh.
This means that we recursed back to the root of the export, and we
can just perform a rootfh lookup.
Also added a modified lookup for volatile file handle recovery. This
function will not use the exception handling on FHEXPIRED errors
and just return FHEXPIRED instead.
v3:
- Add support for getattr recovery by calling nfs4_vfh_getdentry() when a
file handle is passed in from nfs4_exception.
v2:
- Added credentials to the lookup request on an ACCESS error and
if a credential is passed into recovery.
Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/nfs4proc.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 143 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index ee62c1e..9edf9ae 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -80,6 +80,7 @@ static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+static int nfs4_fhexpired_recovery(struct nfs_server *server, struct nfs4_exception *exception);
static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
struct nfs_fattr *fattr, struct iattr *sattr,
struct nfs4_state *state);
@@ -2649,6 +2650,148 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
return status;
}
+static int _nfs4_proc_vfh_lookup(struct rpc_clnt *clnt, struct inode *dir,
+ const struct qstr *name, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr, struct rpc_cred *cred)
+{
+ struct nfs_server *server = NFS_SERVER(dir);
+ int status;
+ struct nfs4_lookup_arg args = {
+ .bitmask = server->attr_bitmask,
+ .dir_fh = NFS_FH(dir),
+ .name = name,
+ };
+ struct nfs4_lookup_res res = {
+ .server = server,
+ .fattr = fattr,
+ .fh = fhandle,
+ };
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP],
+ .rpc_argp = &args,
+ .rpc_resp = &res,
+ .rpc_cred = cred,
+ };
+
+ nfs_fattr_init(fattr);
+
+ dprintk("NFS call lookup %s\n", name->name);
+ status = nfs4_call_sync(clnt, server, &msg, &args.seq_args, &res.seq_res, 0);
+ dprintk("NFS reply lookup: %d\n", status);
+ return status;
+}
+
+static int nfs4_proc_vfh_lookup(struct rpc_clnt *clnt, struct inode *dir,
+ struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+ struct rpc_cred *cred)
+{
+ struct nfs4_exception exception = { };
+ int err;
+ do {
+ int status;
+ if (cred)
+ status = _nfs4_proc_vfh_lookup(clnt, dir, name,
+ fhandle, fattr, cred);
+ else
+ status = _nfs4_proc_lookup(clnt, dir, name, fhandle,
+ fattr);
+ switch (status) {
+ case -NFS4ERR_BADNAME:
+ return -ENOENT;
+ case -NFS4ERR_MOVED:
+ err = nfs4_get_referral(dir, name, fattr, fhandle);
+ break;
+ case -NFS4ERR_FHEXPIRED:
+ return -NFS4ERR_FHEXPIRED;
+ case -NFS4ERR_WRONGSEC:
+ nfs_fixup_secinfo_attributes(fattr, fhandle);
+ }
+ err = nfs4_handle_exception(NFS_SERVER(dir),
+ status, &exception);
+ } while (exception.retry);
+ return err;
+}
+
+static int _nfs4_fhexpired_recovery(struct nfs_server *server, struct dentry *d_parent, struct qstr *name, struct inode *inode, struct rpc_cred *cred)
+{
+ int err;
+ struct nfs_fh *fhandle = NFS_FH(inode);
+ struct nfs_fattr *fattr = nfs_alloc_fattr();
+ if (fattr == NULL)
+ return -ENOMEM;
+ fattr->fileid = 0;
+ if (!nfs_compare_fh(fhandle, server->rootfh)) {
+ struct nfs_fsinfo info = {
+ .fattr = fattr,
+ };
+ err = nfs4_proc_get_root(server, fhandle, &info);
+ if (!err) {
+ if (NFS_FILEID(inode) != info.fattr->fileid)
+ set_nfs_fileid(inode, info.fattr->fileid);
+ nfs_copy_fh(server->rootfh, fhandle);
+ /* Only needed if fsid changes on server */
+ memcpy(&server->fsid, &info.fattr->fsid,
+ sizeof(server->fsid));
+ }
+ nfs_free_fattr(fattr);
+ return err;
+ }
+ err = nfs4_proc_vfh_lookup(server->client, d_parent->d_inode, name,
+ fhandle, fattr, cred);
+ if (!fattr->fileid && !err && fattr->fileid != NFS_FILEID(inode))
+ set_nfs_fileid(inode, fattr->fileid);
+ if (err == -NFS4ERR_FHEXPIRED) {
+ err = _nfs4_fhexpired_recovery(server, d_parent->d_parent,
+ &d_parent->d_name, d_parent->d_inode, cred);
+ if (!err) {
+ err = nfs4_proc_vfh_lookup(server->client,
+ d_parent->d_inode, name,
+ fhandle, fattr, cred);
+ if (!fattr->fileid && !err &&
+ fattr->fileid != NFS_FILEID(inode))
+ set_nfs_fileid(inode, fattr->fileid);
+ }
+ }
+ nfs_free_fattr(fattr);
+ return err;
+}
+
+static int nfs4_fhexpired_recovery(struct nfs_server *server, struct nfs4_exception *exception)
+{
+ int err = 0;
+ struct dentry *dentry = NULL;
+ struct rpc_cred *cred = NULL;
+ if (exception->cred)
+ cred = exception->cred;
+ if (exception->dentry) {
+ dentry = exception->dentry;
+ err = _nfs4_fhexpired_recovery(server, dentry->d_parent,
+ &dentry->d_name, dentry->d_inode, cred);
+ } else if (exception->inode) {
+ dentry = d_obtain_alias(exception->inode);
+ err = _nfs4_fhexpired_recovery(server, dentry->d_parent,
+ &dentry->d_name, exception->inode, cred);
+ dput(dentry);
+ } else if (exception->fhandle) {
+ dentry = nfs4_vfh_getdentry(server, exception->fhandle);
+ if (IS_ERR(dentry) || dentry == NULL)
+ return -ESTALE;
+ err = _nfs4_fhexpired_recovery(server, dentry->d_parent,
+ &dentry->d_name, dentry->d_inode, cred);
+ dput(dentry);
+ } else {
+ BUG_ON(!exception->inode && !exception->dentry
+ && !exception->fhandle);/*Recovery without
+ * VFS objects, missed
+ * a proc function
+ */
+ }
+ if (!err)
+ return -EAGAIN; /* Return EAGAIN so that the operation will
+ be performed again on successful recovery */
+ return err;
+}
+
static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
struct nfs4_exception exception = {
--
1.7.4.4
Rename is a special case because it passes 2 file handles to the server. If the
server replies with NFS4ERR_FHEXPIRED we can't tell which of the to file handles
are expired.
To remedy this, on receiving FHEXPIRED, rename will first call
nfs4_fhexpired_recovery() with the old_dir inode. If this succeeds it will then
call nfs4_fhexpired_recovery() with the new_dir inode. This will bypass
nfs4_handle_exception() on FHEXPIRED errors for renames. Allow us to run
recovery on each of the inodes.
v2:
- Fixed conditional to include check for mount option
Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/nfs4proc.c | 24 +++++++++++++++++++-----
1 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 9edf9ae..a593eef 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3058,13 +3058,27 @@ out:
static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
struct inode *new_dir, struct qstr *new_name)
{
- struct nfs4_exception exception = { };
+ struct nfs4_exception exception = {
+ .inode = old_dir,
+ .dentry = NULL,
+ };
int err;
do {
- err = nfs4_handle_exception(NFS_SERVER(old_dir),
- _nfs4_proc_rename(old_dir, old_name,
- new_dir, new_name),
- &exception);
+ err = _nfs4_proc_rename(old_dir, old_name, new_dir, new_name);
+ if (err == -NFS4ERR_FHEXPIRED && NFS_SERVER(old_dir)->flags
+ & NFS_MOUNT_VFHRETRY) {
+ err = nfs4_fhexpired_recovery(NFS_SERVER(old_dir),
+ &exception);
+ if (err == -EAGAIN) {
+ exception.inode = new_dir;
+ err = nfs4_fhexpired_recovery(
+ NFS_SERVER(old_dir),
+ &exception);
+ }
+ return err;
+ }
+ err = nfs4_handle_exception(NFS_SERVER(old_dir), err,
+ &exception);
} while (exception.retry);
return err;
}
--
1.7.4.4