NFS: Add softreval behaviour to nfs_lookup_revalidate()
authorTrond Myklebust <trondmy@gmail.com>
Tue, 14 Jan 2020 17:06:34 +0000 (12:06 -0500)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Fri, 24 Jan 2020 21:51:13 +0000 (16:51 -0500)
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>
fs/nfs/dir.c
fs/nfs/internal.h
fs/nfs/namespace.c
fs/nfs/nfs3proc.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4namespace.c
fs/nfs/nfs4proc.c
fs/nfs/proc.c
include/linux/nfs_xdr.h

index 9405eeadc3f385fe9a3de59f9adcb83b5b739d2d..bfc66f3f00e179e16569ccde5dc37ecb7d535136 100644 (file)
@@ -1142,10 +1142,17 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
        if (fhandle == NULL || fattr == NULL || IS_ERR(label))
                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 == -ESTALE || ret == -ENOENT)
+               switch (ret) {
+               case -ESTALE:
+               case -ENOENT:
                        ret = 0;
+                       break;
+               case -ETIMEDOUT:
+                       if (NFS_SERVER(inode)->flags & NFS_MOUNT_SOFTREVAL)
+                               ret = 1;
+               }
                goto out;
        }
        ret = 0;
@@ -1408,7 +1415,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
                goto out;
 
        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)
                goto no_entry;
        if (error < 0) {
@@ -1683,7 +1690,7 @@ nfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle,
        d_drop(dentry);
 
        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)
                        goto out_error;
        }
index 17f082442804a0bbf19da2e5a7d7c0ff711d2400..f80c47d5ff277203b19344b33ba51c19a46fcf5e 100644 (file)
@@ -33,6 +33,15 @@ static inline int nfs_attr_use_mounted_on_fileid(struct nfs_fattr *fattr)
        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
  * a server can return, so make something up.
index 4fd22c0d730ca43bb46ec12eec9e0b8640378e42..ad60774049473d3f600267282c275f217aa68510 100644 (file)
@@ -301,7 +301,7 @@ int nfs_submount(struct fs_context *fc, struct nfs_server *server)
        int err;
 
        /* 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,
                                                  NULL);
        dput(parent);
index 4c93a8bca7dc029e250341ddc643e9668366f537..a46d1d5d16d8b24abe7291e3dbd8c75ad9281324 100644 (file)
@@ -154,14 +154,14 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
 }
 
 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 nfs4_label *label)
 {
        struct nfs3_diropargs   arg = {
                .fh             = NFS_FH(dir),
-               .name           = name->name,
-               .len            = name->len
+               .name           = dentry->d_name.name,
+               .len            = dentry->d_name.len
        };
        struct nfs3_diropres    res = {
                .fh             = fhandle,
@@ -173,20 +173,25 @@ nfs3_proc_lookup(struct inode *dir, const struct qstr *name,
                .rpc_resp       = &res,
        };
        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();
        if (res.dir_attr == NULL)
                return -ENOMEM;
 
-       dprintk("NFS call  lookup %s\n", name->name);
        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);
        if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR)) {
                msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR];
                msg.rpc_argp = fhandle;
                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);
        dprintk("NFS reply lookup: %d\n", status);
index 9c136d53987d4ad9a2bd0d4012881bcb2cad606e..c3e669dcee0ea0d3753516e863422b893d7632b6 100644 (file)
@@ -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 *,
                struct page *page, 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 *,
-                           struct nfs_fh *, struct nfs_fattr *);
+extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *,
+                                                   struct dentry *,
+                                                   struct nfs_fh *,
+                                                   struct nfs_fattr *);
 extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
 extern const struct xattr_handler *nfs4_xattr_handlers[];
 extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
index de6875a9b39190d2b59decf900b66b507c27bd7d..3ea1c1008b5bbb23552ce5c32205dc2baa046f18 100644 (file)
@@ -442,12 +442,11 @@ int nfs4_submount(struct fs_context *fc, struct nfs_server *server)
        struct dentry *dentry = ctx->clone_data.dentry;
        struct dentry *parent = dget_parent(dentry);
        struct inode *dir = d_inode(parent);
-       const struct qstr *name = &dentry->d_name;
        struct rpc_clnt *client;
        int ret;
 
        /* 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);
        dput(parent);
        if (IS_ERR(client))
index e178e2e7ad80a0c0bba592af34c2e0733f24fc4b..df38db2eee3b21da0338cb8f8c6c12f240166761 100644 (file)
@@ -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,
-               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_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 = {
                .bitmask = server->attr_bitmask,
                .dir_fh = NFS_FH(dir),
-               .name = name,
+               .name = &dentry->d_name,
        };
        struct nfs4_lookup_res res = {
                .server = server,
@@ -4198,13 +4198,20 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
                .rpc_argp = &args,
                .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);
 
        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 call  lookup %pd2\n", dentry);
+       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);
        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,
-                                  const struct qstr *name, struct nfs_fh *fhandle,
+                                  struct dentry *dentry, struct nfs_fh *fhandle,
                                   struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs4_exception exception = {
                .interruptible = true,
        };
        struct rpc_clnt *client = *clnt;
+       const struct qstr *name = &dentry->d_name;
        int err;
        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);
                switch (err) {
                case -NFS4ERR_BADNAME:
@@ -4262,14 +4270,14 @@ out:
        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 nfs4_label *label)
 {
        int status;
        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)) {
                rpc_shutdown_client(client);
                nfs_fixup_secinfo_attributes(fattr);
@@ -4278,13 +4286,13 @@ static int nfs4_proc_lookup(struct inode *dir, const struct qstr *name,
 }
 
 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 rpc_clnt *client = NFS_CLIENT(dir);
        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)
                return ERR_PTR(status);
        return (client == NFS_CLIENT(dir)) ? rpc_clone_client(client) : client;
index 0451a094e89ee1364af21b8ad3de0d29b869338a..15c865cc837fa22bf9b3e015d4b96cb021c7c049 100644 (file)
@@ -152,14 +152,14 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
 }
 
 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 nfs4_label *label)
 {
        struct nfs_diropargs    arg = {
                .fh             = NFS_FH(dir),
-               .name           = name->name,
-               .len            = name->len
+               .name           = dentry->d_name.name,
+               .len            = dentry->d_name.len
        };
        struct nfs_diropok      res = {
                .fh             = fhandle,
@@ -171,10 +171,15 @@ nfs_proc_lookup(struct inode *dir, const struct qstr *name,
                .rpc_resp       = &res,
        };
        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);
-       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);
        return status;
 }
index 0a36c6f62b58df7566a735b5350e98665e6c4440..94c77ed55ce1b4177f9382121769ac5eb4ea1f3a 100644 (file)
@@ -1661,7 +1661,7 @@ struct nfs_rpc_ops {
                            struct inode *);
        int     (*setattr) (struct dentry *, struct nfs_fattr *,
                            struct iattr *);
-       int     (*lookup)  (struct inode *, const struct qstr *,
+       int     (*lookup)  (struct inode *, struct dentry *,
                            struct nfs_fh *, struct nfs_fattr *,
                            struct nfs4_label *);
        int     (*lookupp) (struct inode *, struct nfs_fh *,