Merge tag 'nfs-for-4.7-1' of git://git.linux-nfs.org/projects/anna/linux-nfs
[cascardo/linux.git] / fs / nfs / nfs4proc.c
index 9795725..223982e 100644 (file)
@@ -427,6 +427,7 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
                case -NFS4ERR_DELAY:
                        nfs_inc_server_stats(server, NFSIOS_DELAY);
                case -NFS4ERR_GRACE:
+               case -NFS4ERR_RECALLCONFLICT:
                        exception->delay = 1;
                        return 0;
 
@@ -2692,6 +2693,7 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                .rpc_resp       = &res,
                .rpc_cred       = cred,
         };
+       struct rpc_cred *delegation_cred = NULL;
        unsigned long timestamp = jiffies;
        fmode_t fmode;
        bool truncate;
@@ -2707,7 +2709,7 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        truncate = (sattr->ia_valid & ATTR_SIZE) ? true : false;
        fmode = truncate ? FMODE_WRITE : FMODE_READ;
 
-       if (nfs4_copy_delegation_stateid(&arg.stateid, inode, fmode)) {
+       if (nfs4_copy_delegation_stateid(inode, fmode, &arg.stateid, &delegation_cred)) {
                /* Use that stateid */
        } else if (truncate && state != NULL) {
                struct nfs_lockowner lockowner = {
@@ -2716,13 +2718,17 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                };
                if (!nfs4_valid_open_stateid(state))
                        return -EBADF;
-               if (nfs4_select_rw_stateid(&arg.stateid, state, FMODE_WRITE,
-                               &lockowner) == -EIO)
+               if (nfs4_select_rw_stateid(state, FMODE_WRITE, &lockowner,
+                               &arg.stateid, &delegation_cred) == -EIO)
                        return -EBADF;
        } else
                nfs4_stateid_copy(&arg.stateid, &zero_stateid);
+       if (delegation_cred)
+               msg.rpc_cred = delegation_cred;
 
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
+
+       put_rpccred(delegation_cred);
        if (status == 0 && state != NULL)
                renew_lease(server, timestamp);
        trace_nfs4_setattr(inode, &arg.stateid, status);
@@ -3793,7 +3799,7 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
 
 static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data)
 {
-       nfs4_setup_sequence(NFS_SERVER(data->dir),
+       nfs4_setup_sequence(NFS_SB(data->dentry->d_sb),
                        &data->args.seq_args,
                        &data->res.seq_res,
                        task);
@@ -4301,7 +4307,7 @@ int nfs4_set_rw_stateid(nfs4_stateid *stateid,
 
        if (l_ctx != NULL)
                lockowner = &l_ctx->lockowner;
-       return nfs4_select_rw_stateid(stateid, ctx->state, fmode, lockowner);
+       return nfs4_select_rw_stateid(ctx->state, fmode, lockowner, stateid, NULL);
 }
 EXPORT_SYMBOL_GPL(nfs4_set_rw_stateid);
 
@@ -6283,10 +6289,10 @@ static int nfs4_xattr_set_nfs4_acl(const struct xattr_handler *handler,
 }
 
 static int nfs4_xattr_get_nfs4_acl(const struct xattr_handler *handler,
-                                  struct dentry *dentry, const char *key,
-                                  void *buf, size_t buflen)
+                                  struct dentry *unused, struct inode *inode,
+                                  const char *key, void *buf, size_t buflen)
 {
-       return nfs4_proc_get_acl(d_inode(dentry), buf, buflen);
+       return nfs4_proc_get_acl(inode, buf, buflen);
 }
 
 static bool nfs4_xattr_list_nfs4_acl(struct dentry *dentry)
@@ -6308,11 +6314,11 @@ static int nfs4_xattr_set_nfs4_label(const struct xattr_handler *handler,
 }
 
 static int nfs4_xattr_get_nfs4_label(const struct xattr_handler *handler,
-                                    struct dentry *dentry, const char *key,
-                                    void *buf, size_t buflen)
+                                    struct dentry *unused, struct inode *inode,
+                                    const char *key, void *buf, size_t buflen)
 {
        if (security_ismaclabel(key))
-               return nfs4_get_security_label(d_inode(dentry), buf, buflen);
+               return nfs4_get_security_label(inode, buf, buflen);
        return -EOPNOTSUPP;
 }
 
@@ -7371,9 +7377,11 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
  * always set csa_cachethis to FALSE because the current implementation
  * of the back channel DRC only supports caching the CB_SEQUENCE operation.
  */
-static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
+static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args,
+                                   struct rpc_clnt *clnt)
 {
        unsigned int max_rqst_sz, max_resp_sz;
+       unsigned int max_bc_payload = rpc_max_bc_payload(clnt);
 
        max_rqst_sz = NFS_MAX_FILE_IO_SIZE + nfs41_maxwrite_overhead;
        max_resp_sz = NFS_MAX_FILE_IO_SIZE + nfs41_maxread_overhead;
@@ -7391,8 +7399,8 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
                args->fc_attrs.max_ops, args->fc_attrs.max_reqs);
 
        /* Back channel attributes */
-       args->bc_attrs.max_rqst_sz = PAGE_SIZE;
-       args->bc_attrs.max_resp_sz = PAGE_SIZE;
+       args->bc_attrs.max_rqst_sz = max_bc_payload;
+       args->bc_attrs.max_resp_sz = max_bc_payload;
        args->bc_attrs.max_resp_sz_cached = 0;
        args->bc_attrs.max_ops = NFS4_MAX_BACK_CHANNEL_OPS;
        args->bc_attrs.max_reqs = NFS41_BC_MAX_CALLBACKS;
@@ -7496,7 +7504,7 @@ static int _nfs4_proc_create_session(struct nfs_client *clp,
        };
        int status;
 
-       nfs4_init_channel_attrs(&args);
+       nfs4_init_channel_attrs(&args, clp->cl_rpcclient);
        args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN);
 
        status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
@@ -7840,40 +7848,34 @@ nfs4_layoutget_prepare(struct rpc_task *task, void *calldata)
        struct nfs4_layoutget *lgp = calldata;
        struct nfs_server *server = NFS_SERVER(lgp->args.inode);
        struct nfs4_session *session = nfs4_get_session(server);
-       int ret;
 
        dprintk("--> %s\n", __func__);
-       /* Note the is a race here, where a CB_LAYOUTRECALL can come in
-        * right now covering the LAYOUTGET we are about to send.
-        * However, that is not so catastrophic, and there seems
-        * to be no way to prevent it completely.
-        */
-       if (nfs41_setup_sequence(session, &lgp->args.seq_args,
-                               &lgp->res.seq_res, task))
-               return;
-       ret = pnfs_choose_layoutget_stateid(&lgp->args.stateid,
-                                         NFS_I(lgp->args.inode)->layout,
-                                         &lgp->args.range,
-                                         lgp->args.ctx->state);
-       if (ret < 0)
-               rpc_exit(task, ret);
+       nfs41_setup_sequence(session, &lgp->args.seq_args,
+                               &lgp->res.seq_res, task);
+       dprintk("<-- %s\n", __func__);
 }
 
 static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
 {
        struct nfs4_layoutget *lgp = calldata;
+
+       dprintk("--> %s\n", __func__);
+       nfs41_sequence_done(task, &lgp->res.seq_res);
+       dprintk("<-- %s\n", __func__);
+}
+
+static int
+nfs4_layoutget_handle_exception(struct rpc_task *task,
+               struct nfs4_layoutget *lgp, struct nfs4_exception *exception)
+{
        struct inode *inode = lgp->args.inode;
        struct nfs_server *server = NFS_SERVER(inode);
        struct pnfs_layout_hdr *lo;
-       struct nfs4_state *state = NULL;
-       unsigned long timeo, now, giveup;
+       int status = task->tk_status;
 
        dprintk("--> %s tk_status => %d\n", __func__, -task->tk_status);
 
-       if (!nfs41_sequence_done(task, &lgp->res.seq_res))
-               goto out;
-
-       switch (task->tk_status) {
+       switch (status) {
        case 0:
                goto out;
 
@@ -7883,57 +7885,43 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
         * retry go inband.
         */
        case -NFS4ERR_LAYOUTUNAVAILABLE:
-               task->tk_status = -ENODATA;
+               status = -ENODATA;
                goto out;
        /*
         * NFS4ERR_BADLAYOUT means the MDS cannot return a layout of
         * length lgp->args.minlength != 0 (see RFC5661 section 18.43.3).
         */
        case -NFS4ERR_BADLAYOUT:
-               goto out_overflow;
+               status = -EOVERFLOW;
+               goto out;
        /*
         * NFS4ERR_LAYOUTTRYLATER is a conflict with another client
         * (or clients) writing to the same RAID stripe except when
         * the minlength argument is 0 (see RFC5661 section 18.43.3).
+        *
+        * Treat it like we would RECALLCONFLICT -- we retry for a little
+        * while, and then eventually give up.
         */
        case -NFS4ERR_LAYOUTTRYLATER:
-               if (lgp->args.minlength == 0)
-                       goto out_overflow;
-       /*
-        * NFS4ERR_RECALLCONFLICT is when conflict with self (must recall
-        * existing layout before getting a new one).
-        */
-       case -NFS4ERR_RECALLCONFLICT:
-               timeo = rpc_get_timeout(task->tk_client);
-               giveup = lgp->args.timestamp + timeo;
-               now = jiffies;
-               if (time_after(giveup, now)) {
-                       unsigned long delay;
-
-                       /* Delay for:
-                        * - Not less then NFS4_POLL_RETRY_MIN.
-                        * - One last time a jiffie before we give up
-                        * - exponential backoff (time_now minus start_attempt)
-                        */
-                       delay = max_t(unsigned long, NFS4_POLL_RETRY_MIN,
-                                   min((giveup - now - 1),
-                                       now - lgp->args.timestamp));
-
-                       dprintk("%s: NFS4ERR_RECALLCONFLICT waiting %lu\n",
-                               __func__, delay);
-                       rpc_delay(task, delay);
-                       /* Do not call nfs4_async_handle_error() */
-                       goto out_restart;
+               if (lgp->args.minlength == 0) {
+                       status = -EOVERFLOW;
+                       goto out;
                }
-               break;
+               /* Fallthrough */
+       case -NFS4ERR_RECALLCONFLICT:
+               nfs4_handle_exception(server, -NFS4ERR_RECALLCONFLICT,
+                                       exception);
+               status = -ERECALLCONFLICT;
+               goto out;
        case -NFS4ERR_EXPIRED:
        case -NFS4ERR_BAD_STATEID:
+               exception->timeout = 0;
                spin_lock(&inode->i_lock);
                if (nfs4_stateid_match(&lgp->args.stateid,
                                        &lgp->args.ctx->state->stateid)) {
                        spin_unlock(&inode->i_lock);
                        /* If the open stateid was bad, then recover it. */
-                       state = lgp->args.ctx->state;
+                       exception->state = lgp->args.ctx->state;
                        break;
                }
                lo = NFS_I(inode)->layout;
@@ -7946,25 +7934,21 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
                         * with the current stateid.
                         */
                        set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
-                       pnfs_mark_matching_lsegs_invalid(lo, &head, NULL);
+                       pnfs_mark_matching_lsegs_invalid(lo, &head, NULL, 0);
                        spin_unlock(&inode->i_lock);
                        pnfs_free_lseg_list(&head);
                } else
                        spin_unlock(&inode->i_lock);
-               goto out_restart;
+               status = -EAGAIN;
+               goto out;
        }
-       if (nfs4_async_handle_error(task, server, state, &lgp->timeout) == -EAGAIN)
-               goto out_restart;
+
+       status = nfs4_handle_exception(server, status, exception);
+       if (exception->retry)
+               status = -EAGAIN;
 out:
        dprintk("<-- %s\n", __func__);
-       return;
-out_restart:
-       task->tk_status = 0;
-       rpc_restart_call_prepare(task);
-       return;
-out_overflow:
-       task->tk_status = -EOVERFLOW;
-       goto out;
+       return status;
 }
 
 static size_t max_response_pages(struct nfs_server *server)
@@ -8033,7 +8017,7 @@ static const struct rpc_call_ops nfs4_layoutget_call_ops = {
 };
 
 struct pnfs_layout_segment *
-nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
+nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout, gfp_t gfp_flags)
 {
        struct inode *inode = lgp->args.inode;
        struct nfs_server *server = NFS_SERVER(inode);
@@ -8053,6 +8037,7 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
                .flags = RPC_TASK_ASYNC,
        };
        struct pnfs_layout_segment *lseg = NULL;
+       struct nfs4_exception exception = { .timeout = *timeout };
        int status = 0;
 
        dprintk("--> %s\n", __func__);
@@ -8066,7 +8051,6 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
                return ERR_PTR(-ENOMEM);
        }
        lgp->args.layout.pglen = max_pages * PAGE_SIZE;
-       lgp->args.timestamp = jiffies;
 
        lgp->res.layoutp = &lgp->args.layout;
        lgp->res.seq_res.sr_slot = NULL;
@@ -8076,13 +8060,17 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
        if (IS_ERR(task))
                return ERR_CAST(task);
        status = nfs4_wait_for_completion_rpc_task(task);
-       if (status == 0)
-               status = task->tk_status;
+       if (status == 0) {
+               status = nfs4_layoutget_handle_exception(task, lgp, &exception);
+               *timeout = exception.timeout;
+       }
+
        trace_nfs4_layoutget(lgp->args.ctx,
                        &lgp->args.range,
                        &lgp->res.range,
                        &lgp->res.stateid,
                        status);
+
        /* if layoutp->len is 0, nfs4_layoutget_prepare called rpc_exit */
        if (status == 0 && lgp->res.layoutp->len)
                lseg = pnfs_layout_process(lgp);
@@ -8138,7 +8126,8 @@ static void nfs4_layoutreturn_release(void *calldata)
 
        dprintk("--> %s\n", __func__);
        spin_lock(&lo->plh_inode->i_lock);
-       pnfs_mark_matching_lsegs_invalid(lo, &freeme, &lrp->args.range);
+       pnfs_mark_matching_lsegs_invalid(lo, &freeme, &lrp->args.range,
+                       be32_to_cpu(lrp->args.stateid.seqid));
        pnfs_mark_layout_returned_if_empty(lo);
        if (lrp->res.lrs_present)
                pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
@@ -8673,6 +8662,9 @@ nfs41_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
 static bool nfs41_match_stateid(const nfs4_stateid *s1,
                const nfs4_stateid *s2)
 {
+       if (s1->type != s2->type)
+               return false;
+
        if (memcmp(s1->other, s2->other, sizeof(s1->other)) != 0)
                return false;