NFS: Add softreval behaviour to nfs_lookup_revalidate()
If the server is unavaliable, we want to allow the revalidating lookup to time out, and to default to validating the cached dentry if the 'softreval' mount option is set. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
parent
fe1e8dbec1
commit
f7b37b8b13
15
fs/nfs/dir.c
15
fs/nfs/dir.c
|
@ -1142,10 +1142,17 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
|
||||||
if (fhandle == NULL || fattr == NULL || IS_ERR(label))
|
if (fhandle == NULL || fattr == NULL || IS_ERR(label))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
|
ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret == -ESTALE || ret == -ENOENT)
|
switch (ret) {
|
||||||
|
case -ESTALE:
|
||||||
|
case -ENOENT:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case -ETIMEDOUT:
|
||||||
|
if (NFS_SERVER(inode)->flags & NFS_MOUNT_SOFTREVAL)
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
@ -1408,7 +1415,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
trace_nfs_lookup_enter(dir, dentry, flags);
|
trace_nfs_lookup_enter(dir, dentry, flags);
|
||||||
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
|
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
|
||||||
if (error == -ENOENT)
|
if (error == -ENOENT)
|
||||||
goto no_entry;
|
goto no_entry;
|
||||||
if (error < 0) {
|
if (error < 0) {
|
||||||
|
@ -1683,7 +1690,7 @@ nfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle,
|
||||||
d_drop(dentry);
|
d_drop(dentry);
|
||||||
|
|
||||||
if (fhandle->size == 0) {
|
if (fhandle->size == 0) {
|
||||||
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, NULL);
|
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, NULL);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_error;
|
goto out_error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,15 @@ static inline int nfs_attr_use_mounted_on_fileid(struct nfs_fattr *fattr)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool nfs_lookup_is_soft_revalidate(const struct dentry *dentry)
|
||||||
|
{
|
||||||
|
if (!(NFS_SB(dentry->d_sb)->flags & NFS_MOUNT_SOFTREVAL))
|
||||||
|
return false;
|
||||||
|
if (!d_is_positive(dentry) || !NFS_FH(d_inode(dentry))->size)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: RFC 1813 doesn't limit the number of auth flavors that
|
* Note: RFC 1813 doesn't limit the number of auth flavors that
|
||||||
* a server can return, so make something up.
|
* a server can return, so make something up.
|
||||||
|
|
|
@ -301,7 +301,7 @@ int nfs_submount(struct fs_context *fc, struct nfs_server *server)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* Look it up again to get its attributes */
|
/* Look it up again to get its attributes */
|
||||||
err = server->nfs_client->rpc_ops->lookup(d_inode(parent), &dentry->d_name,
|
err = server->nfs_client->rpc_ops->lookup(d_inode(parent), dentry,
|
||||||
ctx->mntfh, ctx->clone_data.fattr,
|
ctx->mntfh, ctx->clone_data.fattr,
|
||||||
NULL);
|
NULL);
|
||||||
dput(parent);
|
dput(parent);
|
||||||
|
|
|
@ -154,14 +154,14 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nfs3_proc_lookup(struct inode *dir, const struct qstr *name,
|
nfs3_proc_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr,
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr,
|
||||||
struct nfs4_label *label)
|
struct nfs4_label *label)
|
||||||
{
|
{
|
||||||
struct nfs3_diropargs arg = {
|
struct nfs3_diropargs arg = {
|
||||||
.fh = NFS_FH(dir),
|
.fh = NFS_FH(dir),
|
||||||
.name = name->name,
|
.name = dentry->d_name.name,
|
||||||
.len = name->len
|
.len = dentry->d_name.len
|
||||||
};
|
};
|
||||||
struct nfs3_diropres res = {
|
struct nfs3_diropres res = {
|
||||||
.fh = fhandle,
|
.fh = fhandle,
|
||||||
|
@ -173,20 +173,25 @@ nfs3_proc_lookup(struct inode *dir, const struct qstr *name,
|
||||||
.rpc_resp = &res,
|
.rpc_resp = &res,
|
||||||
};
|
};
|
||||||
int status;
|
int status;
|
||||||
|
unsigned short task_flags = 0;
|
||||||
|
|
||||||
|
/* Is this is an attribute revalidation, subject to softreval? */
|
||||||
|
if (nfs_lookup_is_soft_revalidate(dentry))
|
||||||
|
task_flags |= RPC_TASK_TIMEOUT;
|
||||||
|
|
||||||
|
dprintk("NFS call lookup %pd2\n", dentry);
|
||||||
res.dir_attr = nfs_alloc_fattr();
|
res.dir_attr = nfs_alloc_fattr();
|
||||||
if (res.dir_attr == NULL)
|
if (res.dir_attr == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
dprintk("NFS call lookup %s\n", name->name);
|
|
||||||
nfs_fattr_init(fattr);
|
nfs_fattr_init(fattr);
|
||||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
status = rpc_call_sync(NFS_CLIENT(dir), &msg, task_flags);
|
||||||
nfs_refresh_inode(dir, res.dir_attr);
|
nfs_refresh_inode(dir, res.dir_attr);
|
||||||
if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR)) {
|
if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR)) {
|
||||||
msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR];
|
msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR];
|
||||||
msg.rpc_argp = fhandle;
|
msg.rpc_argp = fhandle;
|
||||||
msg.rpc_resp = fattr;
|
msg.rpc_resp = fattr;
|
||||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
status = rpc_call_sync(NFS_CLIENT(dir), &msg, task_flags);
|
||||||
}
|
}
|
||||||
nfs_free_fattr(res.dir_attr);
|
nfs_free_fattr(res.dir_attr);
|
||||||
dprintk("NFS reply lookup: %d\n", status);
|
dprintk("NFS reply lookup: %d\n", status);
|
||||||
|
|
|
@ -302,8 +302,10 @@ extern int nfs4_proc_fs_locations(struct rpc_clnt *, struct inode *, const struc
|
||||||
extern int nfs4_proc_get_locations(struct inode *, struct nfs4_fs_locations *,
|
extern int nfs4_proc_get_locations(struct inode *, struct nfs4_fs_locations *,
|
||||||
struct page *page, const struct cred *);
|
struct page *page, const struct cred *);
|
||||||
extern int nfs4_proc_fsid_present(struct inode *, const struct cred *);
|
extern int nfs4_proc_fsid_present(struct inode *, const struct cred *);
|
||||||
extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, const struct qstr *,
|
extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *,
|
||||||
struct nfs_fh *, struct nfs_fattr *);
|
struct dentry *,
|
||||||
|
struct nfs_fh *,
|
||||||
|
struct nfs_fattr *);
|
||||||
extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
|
extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
|
||||||
extern const struct xattr_handler *nfs4_xattr_handlers[];
|
extern const struct xattr_handler *nfs4_xattr_handlers[];
|
||||||
extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
|
extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
|
||||||
|
|
|
@ -442,12 +442,11 @@ int nfs4_submount(struct fs_context *fc, struct nfs_server *server)
|
||||||
struct dentry *dentry = ctx->clone_data.dentry;
|
struct dentry *dentry = ctx->clone_data.dentry;
|
||||||
struct dentry *parent = dget_parent(dentry);
|
struct dentry *parent = dget_parent(dentry);
|
||||||
struct inode *dir = d_inode(parent);
|
struct inode *dir = d_inode(parent);
|
||||||
const struct qstr *name = &dentry->d_name;
|
|
||||||
struct rpc_clnt *client;
|
struct rpc_clnt *client;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Look it up again to get its attributes and sec flavor */
|
/* Look it up again to get its attributes and sec flavor */
|
||||||
client = nfs4_proc_lookup_mountpoint(dir, name, ctx->mntfh,
|
client = nfs4_proc_lookup_mountpoint(dir, dentry, ctx->mntfh,
|
||||||
ctx->clone_data.fattr);
|
ctx->clone_data.fattr);
|
||||||
dput(parent);
|
dput(parent);
|
||||||
if (IS_ERR(client))
|
if (IS_ERR(client))
|
||||||
|
|
|
@ -4177,7 +4177,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
|
static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
|
||||||
const struct qstr *name, struct nfs_fh *fhandle,
|
struct dentry *dentry, struct nfs_fh *fhandle,
|
||||||
struct nfs_fattr *fattr, struct nfs4_label *label)
|
struct nfs_fattr *fattr, struct nfs4_label *label)
|
||||||
{
|
{
|
||||||
struct nfs_server *server = NFS_SERVER(dir);
|
struct nfs_server *server = NFS_SERVER(dir);
|
||||||
|
@ -4185,7 +4185,7 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
|
||||||
struct nfs4_lookup_arg args = {
|
struct nfs4_lookup_arg args = {
|
||||||
.bitmask = server->attr_bitmask,
|
.bitmask = server->attr_bitmask,
|
||||||
.dir_fh = NFS_FH(dir),
|
.dir_fh = NFS_FH(dir),
|
||||||
.name = name,
|
.name = &dentry->d_name,
|
||||||
};
|
};
|
||||||
struct nfs4_lookup_res res = {
|
struct nfs4_lookup_res res = {
|
||||||
.server = server,
|
.server = server,
|
||||||
|
@ -4198,13 +4198,20 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
|
||||||
.rpc_argp = &args,
|
.rpc_argp = &args,
|
||||||
.rpc_resp = &res,
|
.rpc_resp = &res,
|
||||||
};
|
};
|
||||||
|
unsigned short task_flags = 0;
|
||||||
|
|
||||||
|
/* Is this is an attribute revalidation, subject to softreval? */
|
||||||
|
if (nfs_lookup_is_soft_revalidate(dentry))
|
||||||
|
task_flags |= RPC_TASK_TIMEOUT;
|
||||||
|
|
||||||
args.bitmask = nfs4_bitmask(server, label);
|
args.bitmask = nfs4_bitmask(server, label);
|
||||||
|
|
||||||
nfs_fattr_init(fattr);
|
nfs_fattr_init(fattr);
|
||||||
|
|
||||||
dprintk("NFS call lookup %s\n", name->name);
|
dprintk("NFS call lookup %pd2\n", dentry);
|
||||||
status = nfs4_call_sync(clnt, server, &msg, &args.seq_args, &res.seq_res, 0);
|
nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0);
|
||||||
|
status = nfs4_do_call_sync(clnt, server, &msg,
|
||||||
|
&args.seq_args, &res.seq_res, task_flags);
|
||||||
dprintk("NFS reply lookup: %d\n", status);
|
dprintk("NFS reply lookup: %d\n", status);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -4218,16 +4225,17 @@ static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
|
static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
|
||||||
const struct qstr *name, struct nfs_fh *fhandle,
|
struct dentry *dentry, struct nfs_fh *fhandle,
|
||||||
struct nfs_fattr *fattr, struct nfs4_label *label)
|
struct nfs_fattr *fattr, struct nfs4_label *label)
|
||||||
{
|
{
|
||||||
struct nfs4_exception exception = {
|
struct nfs4_exception exception = {
|
||||||
.interruptible = true,
|
.interruptible = true,
|
||||||
};
|
};
|
||||||
struct rpc_clnt *client = *clnt;
|
struct rpc_clnt *client = *clnt;
|
||||||
|
const struct qstr *name = &dentry->d_name;
|
||||||
int err;
|
int err;
|
||||||
do {
|
do {
|
||||||
err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr, label);
|
err = _nfs4_proc_lookup(client, dir, dentry, fhandle, fattr, label);
|
||||||
trace_nfs4_lookup(dir, name, err);
|
trace_nfs4_lookup(dir, name, err);
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case -NFS4ERR_BADNAME:
|
case -NFS4ERR_BADNAME:
|
||||||
|
@ -4262,14 +4270,14 @@ out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs4_proc_lookup(struct inode *dir, const struct qstr *name,
|
static int nfs4_proc_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr,
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr,
|
||||||
struct nfs4_label *label)
|
struct nfs4_label *label)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
struct rpc_clnt *client = NFS_CLIENT(dir);
|
struct rpc_clnt *client = NFS_CLIENT(dir);
|
||||||
|
|
||||||
status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, label);
|
status = nfs4_proc_lookup_common(&client, dir, dentry, fhandle, fattr, label);
|
||||||
if (client != NFS_CLIENT(dir)) {
|
if (client != NFS_CLIENT(dir)) {
|
||||||
rpc_shutdown_client(client);
|
rpc_shutdown_client(client);
|
||||||
nfs_fixup_secinfo_attributes(fattr);
|
nfs_fixup_secinfo_attributes(fattr);
|
||||||
|
@ -4278,13 +4286,13 @@ static int nfs4_proc_lookup(struct inode *dir, const struct qstr *name,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct rpc_clnt *
|
struct rpc_clnt *
|
||||||
nfs4_proc_lookup_mountpoint(struct inode *dir, const struct qstr *name,
|
nfs4_proc_lookup_mountpoint(struct inode *dir, struct dentry *dentry,
|
||||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||||
{
|
{
|
||||||
struct rpc_clnt *client = NFS_CLIENT(dir);
|
struct rpc_clnt *client = NFS_CLIENT(dir);
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, NULL);
|
status = nfs4_proc_lookup_common(&client, dir, dentry, fhandle, fattr, NULL);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return ERR_PTR(status);
|
return ERR_PTR(status);
|
||||||
return (client == NFS_CLIENT(dir)) ? rpc_clone_client(client) : client;
|
return (client == NFS_CLIENT(dir)) ? rpc_clone_client(client) : client;
|
||||||
|
|
|
@ -152,14 +152,14 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nfs_proc_lookup(struct inode *dir, const struct qstr *name,
|
nfs_proc_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr,
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr,
|
||||||
struct nfs4_label *label)
|
struct nfs4_label *label)
|
||||||
{
|
{
|
||||||
struct nfs_diropargs arg = {
|
struct nfs_diropargs arg = {
|
||||||
.fh = NFS_FH(dir),
|
.fh = NFS_FH(dir),
|
||||||
.name = name->name,
|
.name = dentry->d_name.name,
|
||||||
.len = name->len
|
.len = dentry->d_name.len
|
||||||
};
|
};
|
||||||
struct nfs_diropok res = {
|
struct nfs_diropok res = {
|
||||||
.fh = fhandle,
|
.fh = fhandle,
|
||||||
|
@ -171,10 +171,15 @@ nfs_proc_lookup(struct inode *dir, const struct qstr *name,
|
||||||
.rpc_resp = &res,
|
.rpc_resp = &res,
|
||||||
};
|
};
|
||||||
int status;
|
int status;
|
||||||
|
unsigned short task_flags = 0;
|
||||||
|
|
||||||
dprintk("NFS call lookup %s\n", name->name);
|
/* Is this is an attribute revalidation, subject to softreval? */
|
||||||
|
if (nfs_lookup_is_soft_revalidate(dentry))
|
||||||
|
task_flags |= RPC_TASK_TIMEOUT;
|
||||||
|
|
||||||
|
dprintk("NFS call lookup %pd2\n", dentry);
|
||||||
nfs_fattr_init(fattr);
|
nfs_fattr_init(fattr);
|
||||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
status = rpc_call_sync(NFS_CLIENT(dir), &msg, task_flags);
|
||||||
dprintk("NFS reply lookup: %d\n", status);
|
dprintk("NFS reply lookup: %d\n", status);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1661,7 +1661,7 @@ struct nfs_rpc_ops {
|
||||||
struct inode *);
|
struct inode *);
|
||||||
int (*setattr) (struct dentry *, struct nfs_fattr *,
|
int (*setattr) (struct dentry *, struct nfs_fattr *,
|
||||||
struct iattr *);
|
struct iattr *);
|
||||||
int (*lookup) (struct inode *, const struct qstr *,
|
int (*lookup) (struct inode *, struct dentry *,
|
||||||
struct nfs_fh *, struct nfs_fattr *,
|
struct nfs_fh *, struct nfs_fattr *,
|
||||||
struct nfs4_label *);
|
struct nfs4_label *);
|
||||||
int (*lookupp) (struct inode *, struct nfs_fh *,
|
int (*lookupp) (struct inode *, struct nfs_fh *,
|
||||||
|
|
Loading…
Reference in New Issue