2011-12-09 15:24:27

by Matthew Treinish

[permalink] [raw]
Subject: [PATCH/RFC v2 0/7] Volatile Filehandle Client-side Support

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.

Simple testing has shown that this approach works and will correctly recover
from a FHEXPIRED error code. However, the current implementation uses
d_obtain_alias if a nfs4_proc function is only given an inode. When hardlinks
are involved, this results in getting a path, but not necessarily a path which
the user has access, which might lead to permission issues.

As Trond pointed out on the first posting originally it was unable to recover
from FHEXPIRED in state_recovery or rpciod since lookup operations started in
either thread context would not have access to perform the lookup. So this
version attempts to fix this by passing an rpc_cred to vfh_lookup. Then the
credential is added to the lookup's rpc message so that the recovery lookup
will have access. The only thing I am unsure of now is where I pass credential
from for rpciod and state recovery contexts.

v2:
- Added rpc_cred passing to the vfh lookup and added handling of access errors
on the lookup

Matthew Treinish (7):
New mount option for volatile filehandle recovery
Added support for FH_EXPIRE_TYPE attribute.
Add VFS objects from nfs4_proc calls into nfs4_exception.
Save root file handle in nfs_server.
Added VFH FHEXPIRED recovery functions.
Perform recovery on both inodes for rename.
Added error handling for NFS4ERR_FHEXPIRED

fs/nfs/client.c | 3 +
fs/nfs/getroot.c | 7 +
fs/nfs/nfs4_fs.h | 3 +
fs/nfs/nfs4proc.c | 287 ++++++++++++++++++++++++++++++++++++++++-----
fs/nfs/nfs4xdr.c | 27 +++++
fs/nfs/super.c | 6 +
include/linux/nfs_fs_sb.h | 2 +
include/linux/nfs_mount.h | 1 +
include/linux/nfs_xdr.h | 1 +
9 files changed, 308 insertions(+), 29 deletions(-)

--
1.7.4.4



2011-12-09 15:23:49

by Matthew Treinish

[permalink] [raw]
Subject: [PATCH/RFC v2 3/7] Add VFS objects from nfs4_proc calls into nfs4_exception.

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.

See nfs4_do_open and nfs4_proc_access for example of how I'm storing
the credentials. I'm not 100% on whether this same method will also apply
for rpciod and state recovery contexts. However, for these examples,
when VFH recovery is initiated by FHEXPIRED being returned for either
operation the credentials are also passed with each lookup.

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 | 3 +
fs/nfs/nfs4proc.c | 117 ++++++++++++++++++++++++++++++++++++++++++----------
2 files changed, 97 insertions(+), 23 deletions(-)

diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 693ae22..772fbf1 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -191,6 +191,9 @@ struct nfs4_exception {
long timeout;
int retry;
struct nfs4_state *state;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct rpc_cred *cred;
};

struct nfs4_state_recovery_ops {
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 6bc393d..5bcc08c 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1239,7 +1239,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);
@@ -1281,7 +1284,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 {
@@ -1666,7 +1672,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 {
@@ -1795,7 +1804,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;

@@ -1886,7 +1899,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,
@@ -2455,7 +2471,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;
@@ -2532,7 +2551,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),
@@ -2588,7 +2611,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),
@@ -2680,7 +2706,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),
@@ -2834,7 +2863,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),
@@ -2926,7 +2958,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),
@@ -2957,7 +2992,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();
@@ -3011,7 +3049,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),
@@ -3059,7 +3100,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();
@@ -3589,7 +3633,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);
@@ -3664,7 +3711,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),
@@ -3927,7 +3977,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);
@@ -4001,7 +4054,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 {
@@ -4405,7 +4461,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 {
@@ -4423,7 +4482,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);
@@ -4501,7 +4563,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 {
@@ -4561,7 +4626,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);
@@ -4768,7 +4836,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


2011-12-09 15:24:05

by Matthew Treinish

[permalink] [raw]
Subject: [PATCH/RFC v2 1/7] New mount option for volatile filehandle recovery

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 1347774..f63229f 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,
@@ -149,6 +150,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" },
@@ -650,6 +652,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;
@@ -1203,6 +1206,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


2011-12-09 15:24:00

by Matthew Treinish

[permalink] [raw]
Subject: [PATCH/RFC v2 6/7] Perform recovery on both inodes for rename.

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 2 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. Allowing 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 4804b39..358f5b0 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2947,13 +2947,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


2011-12-09 15:23:49

by Matthew Treinish

[permalink] [raw]
Subject: [PATCH/RFC v2 4/7] Save root file handle in nfs_server.

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 44dedfe..641f69f 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -1108,6 +1108,7 @@ void nfs_free_server(struct nfs_server *server)
nfs_put_client(server->nfs_client);

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 706c92b..5261fa1 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -157,6 +157,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


2011-12-09 15:23:54

by Matthew Treinish

[permalink] [raw]
Subject: [PATCH/RFC v2 7/7] Added error handling for NFS4ERR_FHEXPIRED

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 358f5b0..7087d42 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -277,6 +277,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


2011-12-09 15:24:09

by Matthew Treinish

[permalink] [raw]
Subject: [PATCH/RFC v2 2/7] Added support for FH_EXPIRE_TYPE attribute.

FH_EXPIRE_TYPE is used by the client to determine
what type of filehandle 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 873bf00..44dedfe 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -957,6 +957,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 be2bbac..6bc393d 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -140,7 +140,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 e6161b2..4d775e2 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4508,6 +4508,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;
@@ -4523,6 +4547,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 b5479df..706c92b 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -112,6 +112,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 2a7c533..7252cab 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -123,6 +123,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


2011-12-09 15:23:58

by Matthew Treinish

[permalink] [raw]
Subject: [PATCH/RFC v2 5/7] Added VFH FHEXPIRED recovery functions.

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 filehandle 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.

v2:
- Added credentials to the lookup request if a credential is passed into recovery.

Signed-off-by: Matthew Treinish <[email protected]>
---
fs/nfs/nfs4proc.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 133 insertions(+), 0 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 5bcc08c..4804b39 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -74,6 +74,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);
@@ -2494,6 +2495,138 @@ static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qst
return err;
}

+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;
+ struct dentry *dentry;
+ 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);
+ }
+ BUG_ON(!exception->inode && !exception->dentry); /*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 nfs_server *server = NFS_SERVER(inode);
--
1.7.4.4